summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--OWNERS1
-rw-r--r--SDK_OWNERS5
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java36
-rw-r--r--boot/boot-image-profile-extra.txt1
-rw-r--r--core/api/current.txt153
-rw-r--r--core/api/module-lib-current.txt1
-rw-r--r--core/api/system-current.txt64
-rw-r--r--core/api/test-current.txt4
-rw-r--r--core/java/android/app/ContextImpl.java18
-rw-r--r--core/java/android/app/IActivityManager.aidl1
-rw-r--r--core/java/android/app/IActivityTaskManager.aidl17
-rw-r--r--core/java/android/app/IBackgroundActivityLaunchCallback.aidl26
-rw-r--r--core/java/android/app/INotificationManager.aidl1
-rw-r--r--core/java/android/app/LoadedApk.java12
-rw-r--r--core/java/android/app/NotificationChannel.java15
-rw-r--r--core/java/android/app/admin/flags/flags.aconfig8
-rw-r--r--core/java/android/app/appfunctions/AppFunctionManager.java112
-rw-r--r--core/java/android/app/appfunctions/ExecuteAppFunctionRequest.java24
-rw-r--r--core/java/android/app/appfunctions/ExecuteAppFunctionResponse.java149
-rw-r--r--core/java/android/app/backup/NotificationLoggingConstants.java46
-rw-r--r--core/java/android/app/contextualsearch/flags.aconfig14
-rw-r--r--core/java/android/appwidget/AppWidgetHost.java45
-rw-r--r--core/java/android/companion/virtual/flags/flags.aconfig8
-rw-r--r--core/java/android/content/Context.java23
-rw-r--r--core/java/android/content/ContextWrapper.java7
-rw-r--r--core/java/android/content/Intent.java25
-rw-r--r--core/java/android/content/om/OverlayManager.java5
-rw-r--r--core/java/android/content/om/OverlayManagerTransaction.java15
-rw-r--r--core/java/android/content/pm/multiuser.aconfig2
-rw-r--r--core/java/android/content/res/AssetManager.java5
-rw-r--r--core/java/android/content/res/flags.aconfig9
-rw-r--r--core/java/android/content/res/loader/ResourcesProvider.java2
-rw-r--r--core/java/android/database/sqlite/SQLiteConnection.java3
-rw-r--r--core/java/android/hardware/biometrics/BiometricPrompt.java9
-rw-r--r--core/java/android/hardware/biometrics/PromptContentItem.java5
-rw-r--r--core/java/android/hardware/biometrics/PromptContentItemBulletedText.java4
-rw-r--r--core/java/android/hardware/biometrics/PromptContentItemParcelable.java4
-rw-r--r--core/java/android/hardware/biometrics/PromptContentItemPlainText.java4
-rw-r--r--core/java/android/hardware/biometrics/PromptContentView.java5
-rw-r--r--core/java/android/hardware/biometrics/PromptContentViewParcelable.java4
-rw-r--r--core/java/android/hardware/biometrics/PromptContentViewWithMoreOptionsButton.java3
-rw-r--r--core/java/android/hardware/biometrics/PromptInfo.java2
-rw-r--r--core/java/android/hardware/biometrics/PromptVerticalListContentView.java4
-rw-r--r--core/java/android/hardware/camera2/CameraCharacteristics.java40
-rw-r--r--core/java/android/hardware/camera2/CameraMetadata.java16
-rw-r--r--core/java/android/hardware/camera2/CaptureRequest.java60
-rw-r--r--core/java/android/hardware/camera2/CaptureResult.java60
-rw-r--r--core/java/android/hardware/camera2/params/OutputConfiguration.java2
-rw-r--r--core/java/android/hardware/display/DisplayManager.java8
-rw-r--r--core/java/android/hardware/display/DisplayManagerGlobal.java75
-rw-r--r--core/java/android/hardware/display/VirtualDisplayConfig.java48
-rw-r--r--core/java/android/hardware/input/KeyGestureEvent.java26
-rw-r--r--core/java/android/os/CombinedMessageQueue/MessageQueue.java114
-rw-r--r--core/java/android/os/ConcurrentMessageQueue/MessageQueue.java22
-rw-r--r--core/java/android/os/StrictMode.java81
-rw-r--r--core/java/android/os/strictmode/BackgroundActivityLaunchViolation.java41
-rw-r--r--core/java/android/permission/flags.aconfig18
-rw-r--r--core/java/android/provider/Settings.java22
-rw-r--r--core/java/android/security/advancedprotection/AdvancedProtectionFeature.aidl23
-rw-r--r--core/java/android/security/advancedprotection/AdvancedProtectionFeature.java77
-rw-r--r--core/java/android/security/advancedprotection/AdvancedProtectionManager.java19
-rw-r--r--core/java/android/security/advancedprotection/IAdvancedProtectionService.aidl3
-rw-r--r--core/java/android/security/responsible_apis_flags.aconfig8
-rw-r--r--core/java/android/service/contextualsearch/OWNERS3
-rw-r--r--core/java/android/service/notification/NotificationListenerService.java32
-rw-r--r--core/java/android/service/notification/flags.aconfig8
-rw-r--r--core/java/android/service/wallpaper/WallpaperService.java4
-rw-r--r--core/java/android/text/flags/flags.aconfig7
-rw-r--r--core/java/android/view/SurfaceControl.java13
-rw-r--r--core/java/android/view/SurfaceView.java34
-rw-r--r--core/java/android/view/View.java139
-rw-r--r--core/java/android/view/accessibility/AccessibilityEvent.java24
-rw-r--r--core/java/android/view/accessibility/AccessibilityNodeInfo.java65
-rw-r--r--core/java/android/view/accessibility/flags/accessibility_flags.aconfig7
-rw-r--r--core/java/android/widget/RemoteViews.java109
-rw-r--r--core/java/android/widget/TextClock.java22
-rw-r--r--core/java/android/window/BackNavigationInfo.java17
-rw-r--r--core/java/android/window/DesktopModeFlags.java7
-rw-r--r--core/java/android/window/flags/responsible_apis.aconfig9
-rw-r--r--core/java/android/window/flags/windowing_frontend.aconfig11
-rw-r--r--core/java/com/android/internal/app/SuggestedLocaleAdapter.java69
-rw-r--r--core/java/com/android/internal/content/om/OverlayConfig.java3
-rw-r--r--core/java/com/android/internal/content/om/OverlayManagerImpl.java20
-rw-r--r--core/java/com/android/internal/jank/FrameTracker.java5
-rw-r--r--core/java/com/android/internal/protolog/ProcessedPerfettoProtoLogImpl.java4
-rw-r--r--core/java/com/android/server/FgThread.java1
-rw-r--r--core/java/com/android/server/ServiceThread.java1
-rw-r--r--core/jni/Android.bp50
-rw-r--r--core/jni/android_view_SurfaceControl.cpp20
-rw-r--r--core/res/AndroidManifest.xml7
-rw-r--r--core/res/res/layout/app_language_picker_locale_item.xml (renamed from core/res/res/layout/app_language_picker_current_locale_item.xml)37
-rw-r--r--core/res/res/values-en-rCA/strings.xml15
-rw-r--r--core/res/res/values-it/strings.xml15
-rw-r--r--core/res/res/values-ne/strings.xml2
-rw-r--r--core/res/res/values-pt-rPT/strings.xml15
-rw-r--r--core/res/res/values-te/strings.xml2
-rw-r--r--core/res/res/values/attrs.xml6
-rw-r--r--core/res/res/values/public-staging.xml2
-rw-r--r--core/res/res/values/symbols.xml2
-rw-r--r--core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java2
-rw-r--r--core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java8
-rw-r--r--core/tests/overlaytests/device_self_targeting/src/com/android/overlaytest/OverlayManagerImplTest.java15
-rw-r--r--graphics/java/android/graphics/Paint.java46
-rw-r--r--graphics/java/android/graphics/RenderNode.java13
-rw-r--r--graphics/java/android/graphics/RuntimeColorFilter.java305
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/DividerPresenter.java28
-rw-r--r--libs/WindowManager/Shell/multivalentScreenshotTests/Android.bp36
-rw-r--r--libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleStackViewTest.kt25
-rw-r--r--libs/WindowManager/Shell/res/layout/compat_ui_layout.xml4
-rw-r--r--libs/WindowManager/Shell/res/layout/user_aspect_ratio_settings_layout.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-ar/strings.xml6
-rw-r--r--libs/WindowManager/Shell/res/values-as/strings.xml6
-rw-r--r--libs/WindowManager/Shell/res/values-bn/strings.xml6
-rw-r--r--libs/WindowManager/Shell/res/values-bs/strings.xml6
-rw-r--r--libs/WindowManager/Shell/res/values-cs/strings.xml6
-rw-r--r--libs/WindowManager/Shell/res/values-el/strings.xml6
-rw-r--r--libs/WindowManager/Shell/res/values-en-rAU/strings.xml6
-rw-r--r--libs/WindowManager/Shell/res/values-en-rGB/strings.xml6
-rw-r--r--libs/WindowManager/Shell/res/values-en-rIN/strings.xml6
-rw-r--r--libs/WindowManager/Shell/res/values-hi/strings.xml6
-rw-r--r--libs/WindowManager/Shell/res/values-hr/strings.xml6
-rw-r--r--libs/WindowManager/Shell/res/values-is/strings.xml6
-rw-r--r--libs/WindowManager/Shell/res/values-it/strings.xml6
-rw-r--r--libs/WindowManager/Shell/res/values-ja/strings.xml6
-rw-r--r--libs/WindowManager/Shell/res/values-ka/strings.xml6
-rw-r--r--libs/WindowManager/Shell/res/values-km/strings.xml6
-rw-r--r--libs/WindowManager/Shell/res/values-kn/strings.xml6
-rw-r--r--libs/WindowManager/Shell/res/values-lt/strings.xml6
-rw-r--r--libs/WindowManager/Shell/res/values-mr/strings.xml6
-rw-r--r--libs/WindowManager/Shell/res/values-ms/strings.xml6
-rw-r--r--libs/WindowManager/Shell/res/values-ne/strings.xml6
-rw-r--r--libs/WindowManager/Shell/res/values-pl/strings.xml6
-rw-r--r--libs/WindowManager/Shell/res/values-pt-rPT/strings.xml6
-rw-r--r--libs/WindowManager/Shell/res/values-sl/strings.xml6
-rw-r--r--libs/WindowManager/Shell/res/values-ta/strings.xml6
-rw-r--r--libs/WindowManager/Shell/res/values-te/strings.xml6
-rw-r--r--libs/WindowManager/Shell/res/values-th/strings.xml6
-rw-r--r--libs/WindowManager/Shell/res/values-tl/strings.xml6
-rw-r--r--libs/WindowManager/Shell/res/values-zh-rCN/strings.xml6
-rw-r--r--libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/pip/PipContentOverlay.java39
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java54
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java30
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerSnapAlgorithm.java73
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java52
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUILayout.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/compatui/UserAspectRatioSettingsLayout.java4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java9
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopImmersiveController.kt5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandler.kt64
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt10
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt20
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ExitDesktopTaskTransitionHandler.java3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/persistence/DesktopRepositoryInitializerImpl.kt4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java79
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java104
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java8
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java24
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTransition.java6
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipResizeAnimator.java89
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/recents/IRecentTasksListener.aidl3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java26
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/recents/TaskStackTransitionObserver.kt17
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java14
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java31
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java82
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopStatusBarInputLayerSupplier.kt117
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java16
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/additionalviewcontainer/AdditionalSystemViewContainer.kt92
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDecorViewModel.kt26
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDividerWindowManager.kt26
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecoration.kt4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/TilingDividerView.kt2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolder.kt109
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java12
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandlerTest.kt163
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt12
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/ExitDesktopTaskTransitionHandlerTest.java40
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/education/AppHandleEducationControllerTest.kt1
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java2
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipResizeAnimatorTest.java276
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/TaskStackTransitionObserverTest.kt166
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt4
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java10
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java8
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDecorViewModelTest.kt27
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDividerWindowManagerTest.kt30
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecorationTest.kt2
-rw-r--r--libs/appfunctions/api/current.txt23
-rw-r--r--libs/appfunctions/java/com/google/android/appfunctions/sidecar/AppFunctionManager.java27
-rw-r--r--libs/appfunctions/java/com/google/android/appfunctions/sidecar/ExecuteAppFunctionResponse.java141
-rw-r--r--libs/appfunctions/tests/src/com/google/android/appfunctions/sidecar/tests/SidecarConverterTest.kt8
-rw-r--r--libs/hwui/Android.bp2
-rw-r--r--libs/hwui/ColorFilter.h31
-rw-r--r--libs/hwui/hwui/ImageDecoder.cpp4
-rw-r--r--libs/hwui/jni/BitmapFactory.cpp4
-rw-r--r--libs/hwui/jni/BitmapRegionDecoder.cpp4
-rw-r--r--libs/hwui/jni/ColorFilter.cpp93
-rw-r--r--libs/hwui/jni/ImageDecoder.cpp4
-rw-r--r--libs/hwui/jni/RuntimeEffectUtils.cpp101
-rw-r--r--libs/hwui/jni/RuntimeEffectUtils.h37
-rw-r--r--libs/hwui/jni/android_graphics_RenderNode.cpp10
-rw-r--r--libs/hwui/libhwui.map.txt1
-rw-r--r--libs/hwui/utils/StatsUtils.cpp102
-rw-r--r--libs/hwui/utils/StatsUtils.h33
-rw-r--r--media/java/android/media/AudioAttributes.java36
-rw-r--r--media/java/android/media/AudioDeviceInfo.java33
-rw-r--r--media/java/android/media/AudioManager.java5
-rw-r--r--media/java/android/media/AudioSystem.java7
-rw-r--r--media/java/android/media/MediaCodecInfo.java260
-rw-r--r--media/java/android/media/MediaFormat.java3
-rw-r--r--media/java/android/media/MediaRecorder.java4
-rw-r--r--native/graphics/jni/Android.bp9
-rw-r--r--native/graphics/jni/imagedecoder.cpp41
-rw-r--r--packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/HandlerExecutor.kt2
-rw-r--r--packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/KeyedObserver.kt4
-rw-r--r--packages/SettingsLib/Graph/graph.proto3
-rw-r--r--packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceGraphBuilder.kt39
-rw-r--r--packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceMetadata.kt11
-rw-r--r--packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceScreenBindingHelper.kt3
-rw-r--r--packages/SettingsLib/res/values-bs/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-in/arrays.xml4
-rw-r--r--packages/SettingsLib/res/values-in/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-ky/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-mk/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-or/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-sk/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-sv/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-th/strings.xml2
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingId.java6
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingRepository.kt42
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingRepositoryTest.kt35
-rw-r--r--packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java1
-rw-r--r--packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java3
-rw-r--r--packages/SystemUI/AndroidManifest.xml4
-rw-r--r--packages/SystemUI/aconfig/systemui.aconfig12
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/OverlayShade.kt6
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt62
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/PredictiveBackHandler.kt1
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt1
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/UserActionDistanceScopeImpl.kt14
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/AnchoredSize.kt12
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/AnchoredTranslate.kt18
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/DrawScale.kt9
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/EdgeTranslate.kt20
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Fade.kt9
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/ScaleSize.kt9
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Transformation.kt30
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Translate.kt21
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transition/Seek.kt23
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt98
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryLocalImplTest.kt (renamed from packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryImplTest.kt)8
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/communal/shared/model/CommunalWidgetContentModelTest.kt81
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/widgets/CommunalAppWidgetHostTest.kt2
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostStartableTest.kt55
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/CommunalWidgetHostTest.kt63
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/GlanceableHubWidgetManagerServiceTest.kt319
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/WidgetConfigurationControllerTest.kt10
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractorTest.kt2
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorTest.kt82
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/composefragment/viewmodel/AbstractQSFragmentComposeViewModelTest.kt12
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModelTest.kt103
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/QuickQuickSettingsViewModelTest.kt45
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/CommandQueueInitializerTest.kt2
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt9
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java7
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManagerTest.kt19
-rw-r--r--packages/SystemUI/res/values-af/strings.xml8
-rw-r--r--packages/SystemUI/res/values-am/strings.xml8
-rw-r--r--packages/SystemUI/res/values-ar/strings.xml8
-rw-r--r--packages/SystemUI/res/values-as/strings.xml14
-rw-r--r--packages/SystemUI/res/values-az/strings.xml8
-rw-r--r--packages/SystemUI/res/values-b+sr+Latn/strings.xml14
-rw-r--r--packages/SystemUI/res/values-be/strings.xml8
-rw-r--r--packages/SystemUI/res/values-bg/strings.xml8
-rw-r--r--packages/SystemUI/res/values-bn/strings.xml14
-rw-r--r--packages/SystemUI/res/values-bs/strings.xml14
-rw-r--r--packages/SystemUI/res/values-ca/strings.xml8
-rw-r--r--packages/SystemUI/res/values-cs/strings.xml8
-rw-r--r--packages/SystemUI/res/values-da/strings.xml14
-rw-r--r--packages/SystemUI/res/values-de/strings.xml8
-rw-r--r--packages/SystemUI/res/values-el/strings.xml8
-rw-r--r--packages/SystemUI/res/values-en-rAU/strings.xml8
-rw-r--r--packages/SystemUI/res/values-en-rCA/strings.xml23
-rw-r--r--packages/SystemUI/res/values-en-rGB/strings.xml8
-rw-r--r--packages/SystemUI/res/values-en-rIN/strings.xml8
-rw-r--r--packages/SystemUI/res/values-es-rUS/strings.xml14
-rw-r--r--packages/SystemUI/res/values-es/strings.xml8
-rw-r--r--packages/SystemUI/res/values-et/strings.xml8
-rw-r--r--packages/SystemUI/res/values-eu/strings.xml8
-rw-r--r--packages/SystemUI/res/values-fa/strings.xml14
-rw-r--r--packages/SystemUI/res/values-fi/strings.xml8
-rw-r--r--packages/SystemUI/res/values-fr-rCA/strings.xml8
-rw-r--r--packages/SystemUI/res/values-fr/strings.xml8
-rw-r--r--packages/SystemUI/res/values-gl/strings.xml8
-rw-r--r--packages/SystemUI/res/values-gu/strings.xml14
-rw-r--r--packages/SystemUI/res/values-hi/strings.xml8
-rw-r--r--packages/SystemUI/res/values-hr/strings.xml8
-rw-r--r--packages/SystemUI/res/values-hu/strings.xml8
-rw-r--r--packages/SystemUI/res/values-hy/strings.xml8
-rw-r--r--packages/SystemUI/res/values-in/strings.xml12
-rw-r--r--packages/SystemUI/res/values-is/strings.xml14
-rw-r--r--packages/SystemUI/res/values-it/strings.xml23
-rw-r--r--packages/SystemUI/res/values-iw/strings.xml14
-rw-r--r--packages/SystemUI/res/values-ja/strings.xml8
-rw-r--r--packages/SystemUI/res/values-ka/strings.xml8
-rw-r--r--packages/SystemUI/res/values-kk/strings.xml8
-rw-r--r--packages/SystemUI/res/values-km/strings.xml8
-rw-r--r--packages/SystemUI/res/values-kn/strings.xml14
-rw-r--r--packages/SystemUI/res/values-ko/strings.xml16
-rw-r--r--packages/SystemUI/res/values-ky/strings.xml8
-rw-r--r--packages/SystemUI/res/values-lo/strings.xml8
-rw-r--r--packages/SystemUI/res/values-lt/strings.xml8
-rw-r--r--packages/SystemUI/res/values-lv/strings.xml8
-rw-r--r--packages/SystemUI/res/values-mk/strings.xml14
-rw-r--r--packages/SystemUI/res/values-ml/strings.xml14
-rw-r--r--packages/SystemUI/res/values-mn/strings.xml14
-rw-r--r--packages/SystemUI/res/values-mr/strings.xml14
-rw-r--r--packages/SystemUI/res/values-ms/strings.xml8
-rw-r--r--packages/SystemUI/res/values-my/strings.xml8
-rw-r--r--packages/SystemUI/res/values-nb/strings.xml8
-rw-r--r--packages/SystemUI/res/values-ne/strings.xml8
-rw-r--r--packages/SystemUI/res/values-nl/strings.xml14
-rw-r--r--packages/SystemUI/res/values-or/strings.xml8
-rw-r--r--packages/SystemUI/res/values-pa/strings.xml14
-rw-r--r--packages/SystemUI/res/values-pl/strings.xml8
-rw-r--r--packages/SystemUI/res/values-pt-rBR/strings.xml8
-rw-r--r--packages/SystemUI/res/values-pt-rPT/strings.xml23
-rw-r--r--packages/SystemUI/res/values-pt/strings.xml8
-rw-r--r--packages/SystemUI/res/values-ro/strings.xml8
-rw-r--r--packages/SystemUI/res/values-ru/strings.xml14
-rw-r--r--packages/SystemUI/res/values-si/strings.xml8
-rw-r--r--packages/SystemUI/res/values-sk/strings.xml14
-rw-r--r--packages/SystemUI/res/values-sl/strings.xml8
-rw-r--r--packages/SystemUI/res/values-sq/strings.xml8
-rw-r--r--packages/SystemUI/res/values-sr/strings.xml14
-rw-r--r--packages/SystemUI/res/values-sv/strings.xml8
-rw-r--r--packages/SystemUI/res/values-sw/strings.xml8
-rw-r--r--packages/SystemUI/res/values-sw600dp/dimens.xml8
-rw-r--r--packages/SystemUI/res/values-ta/strings.xml8
-rw-r--r--packages/SystemUI/res/values-te/strings.xml14
-rw-r--r--packages/SystemUI/res/values-th/strings.xml8
-rw-r--r--packages/SystemUI/res/values-tl/strings.xml8
-rw-r--r--packages/SystemUI/res/values-tr/strings.xml8
-rw-r--r--packages/SystemUI/res/values-uk/strings.xml10
-rw-r--r--packages/SystemUI/res/values-ur/strings.xml8
-rw-r--r--packages/SystemUI/res/values-uz/strings.xml8
-rw-r--r--packages/SystemUI/res/values-vi/strings.xml14
-rw-r--r--packages/SystemUI/res/values-zh-rCN/strings.xml8
-rw-r--r--packages/SystemUI/res/values-zh-rHK/strings.xml8
-rw-r--r--packages/SystemUI/res/values-zh-rTW/strings.xml8
-rw-r--r--packages/SystemUI/res/values-zu/strings.xml8
-rw-r--r--packages/SystemUI/res/values/dimens.xml6
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractor.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt37
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/CredentialViewBinder.kt15
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/CredentialViewModel.kt17
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt107
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.kt17
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalStartableModule.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/dagger/GlanceableHubWidgetManagerModule.kt38
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/data/db/CommunalDatabase.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/data/db/CommunalWidgetDao.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepository.kt12
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryModule.kt19
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryRemoteImpl.kt85
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/domain/model/CommunalContentModel.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalWidgetContentModel.aidl19
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalWidgetContentModel.kt101
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/shared/model/GlanceableHubMultiUserHelper.kt74
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/util/WidgetViewFactory.kt29
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalAppWidgetHost.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostStartable.kt99
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalWidgetHost.kt12
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalWidgetModule.kt12
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/widgets/GlanceableHubWidgetManager.kt171
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/widgets/GlanceableHubWidgetManagerService.kt305
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/widgets/GlanceableHubWidgetManagerServiceInfo.kt35
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/widgets/GlanceableHubWidgetManagerServiceSupplier.kt74
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/widgets/IGlanceableHubWidgetManagerService.aidl51
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/widgets/ServiceWatcherFactory.kt55
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/widgets/WidgetConfigurationController.kt29
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/DefaultServiceBinder.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/source/MultitaskingShortcutsSource.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt24
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSImpl.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt81
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/composefragment/dagger/QSFragmentComposeModule.kt40
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModel.kt82
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PaginatedGridLayout.kt13
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/QuickQuickSettings.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/InfiniteGridLayout.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/InFirstPageViewModel.kt29
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/InfiniteGridViewModel.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/PaginatedGridViewModel.kt59
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/QSColumnsViewModel.kt22
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/QuickQuickSettingsViewModel.kt90
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsContainerViewModel.kt13
-rw-r--r--packages/SystemUI/src/com/android/systemui/smartspace/config/BcSmartspaceConfigProvider.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/core/CommandQueueInitializer.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java66
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt14
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java36
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalWidgetRepository.kt47
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorKosmos.kt3
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/communal/shared/model/FakeGlanceableHubMultiUserHelper.kt48
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/communal/shared/model/GlanceableHubMultiUserHelperKosmos.kt24
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/communal/widgets/GlanceableHubWidgetManagerKosmos.kt22
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt35
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataManagerImplKosmos.kt23
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/MediaDataManagerKosmos.kt21
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerKosmos.kt23
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerLoggerKosmos.kt23
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManagerKosmos.kt11
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/ui/controller/MediaHostStatesManagerKosmos.kt21
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/ui/view/MediaHostKosmos.kt42
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/qs/composefragment/dagger/QSFragmentComposeModuleKosmos.kt21
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModelKosmos.kt10
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/InFirstPageViewModelKosmos.kt21
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/PaginatedGridViewModelKosmos.kt3
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/QuickQuickSettingsViewModelKosmos.kt24
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsContainerViewModelKosmos.kt4
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManagerKosmos.kt2
-rw-r--r--ravenwood/texts/ravenwood-annotation-allowed-classes.txt3
-rw-r--r--services/Android.bp23
-rw-r--r--services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java20
-rw-r--r--services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java25
-rw-r--r--services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java216
-rw-r--r--services/companion/java/com/android/server/companion/CompanionExemptionProcessor.java219
-rw-r--r--services/companion/java/com/android/server/companion/devicepresence/CompanionAppBinder.java5
-rw-r--r--services/companion/java/com/android/server/companion/devicepresence/DevicePresenceProcessor.java27
-rw-r--r--services/core/Android.bp1
-rw-r--r--services/core/java/com/android/server/VcnManagementService.java3
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java8
-rw-r--r--services/core/java/com/android/server/am/BroadcastController.java15
-rw-r--r--services/core/java/com/android/server/am/SettingsToPropertiesMapper.java1
-rw-r--r--services/core/java/com/android/server/audio/AudioDeviceBroker.java14
-rw-r--r--services/core/java/com/android/server/audio/AudioService.java30
-rw-r--r--services/core/java/com/android/server/audio/MediaFocusControl.java1
-rw-r--r--services/core/java/com/android/server/display/DisplayManagerService.java61
-rw-r--r--services/core/java/com/android/server/input/InputGestureManager.java24
-rw-r--r--services/core/java/com/android/server/input/InputManagerService.java1
-rw-r--r--services/core/java/com/android/server/input/InputShellCommand.java24
-rw-r--r--services/core/java/com/android/server/input/KeyGestureController.java232
-rw-r--r--services/core/java/com/android/server/locksettings/LockSettingsService.java21
-rw-r--r--services/core/java/com/android/server/notification/NotificationManagerService.java30
-rw-r--r--services/core/java/com/android/server/pm/BroadcastHelper.java106
-rw-r--r--services/core/java/com/android/server/security/advancedprotection/AdvancedProtectionService.java36
-rw-r--r--services/core/java/com/android/server/security/advancedprotection/features/AdvancedProtectionHook.java4
-rw-r--r--services/core/java/com/android/server/security/advancedprotection/features/AdvancedProtectionProvider.java27
-rw-r--r--services/core/java/com/android/server/stats/pull/netstats/NetworkStatsAccumulator.java24
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecordInputSink.java3
-rw-r--r--services/core/java/com/android/server/wm/ActivityStartController.java2
-rw-r--r--services/core/java/com/android/server/wm/ActivityStarter.java43
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskManagerService.java23
-rw-r--r--services/core/java/com/android/server/wm/AppCompatAspectRatioOverrides.java12
-rw-r--r--services/core/java/com/android/server/wm/AppCompatOrientationPolicy.java13
-rw-r--r--services/core/java/com/android/server/wm/AppCompatSizeCompatModePolicy.java2
-rw-r--r--services/core/java/com/android/server/wm/AppCompatUtils.java18
-rw-r--r--services/core/java/com/android/server/wm/BackNavigationController.java38
-rw-r--r--services/core/java/com/android/server/wm/BackgroundActivityStartController.java126
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java11
-rw-r--r--services/core/java/com/android/server/wm/DisplayWindowSettings.java40
-rw-r--r--services/core/java/com/android/server/wm/EmbeddedWindowController.java36
-rw-r--r--services/core/java/com/android/server/wm/Task.java3
-rw-r--r--services/core/java/com/android/server/wm/WindowContainer.java4
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerInternal.java12
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java14
-rw-r--r--services/core/java/com/android/server/wm/WindowState.java4
-rw-r--r--services/core/java/com/android/server/wm/utils/RegionUtils.java5
-rw-r--r--services/tests/servicestests/src/com/android/server/security/advancedprotection/AdvancedProtectionServiceTest.java96
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java220
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java4
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/AppCompatActivityRobot.java4
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/AppCompatAspectRatioOverridesTest.java98
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerTests.java1
-rw-r--r--telephony/common/com/android/internal/telephony/TelephonyPermissions.java23
-rw-r--r--telephony/java/android/telephony/TelephonyManager.java111
-rw-r--r--telephony/java/android/telephony/ims/ImsCallSessionListener.java12
-rw-r--r--telephony/java/android/telephony/ims/feature/ConnectionFailureInfo.java10
-rw-r--r--telephony/java/android/telephony/ims/feature/ImsTrafficSessionCallback.java10
-rw-r--r--telephony/java/android/telephony/ims/feature/MmTelFeature.java34
-rw-r--r--telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl21
-rw-r--r--test-mock/src/android/test/mock/MockContext.java7
-rw-r--r--tests/Input/src/com/android/server/input/InputShellCommandTest.java15
-rw-r--r--tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt146
-rw-r--r--tests/graphics/HwAccelerationTest/AndroidManifest.xml20
-rw-r--r--tests/graphics/HwAccelerationTest/res/layout/scrolling_zabove_surfaceview.xml131
-rw-r--r--tests/graphics/HwAccelerationTest/src/com/android/test/hwui/ScrollingZAboveScaledSurfaceView.kt58
-rw-r--r--tests/graphics/HwAccelerationTest/src/com/android/test/hwui/ScrollingZAboveSurfaceView.kt57
500 files changed, 10846 insertions, 2716 deletions
diff --git a/OWNERS b/OWNERS
index eb2bfcfdf82d..d0a634e529c5 100644
--- a/OWNERS
+++ b/OWNERS
@@ -47,3 +47,4 @@ per-file MULTIUSER_OWNERS = file:/MULTIUSER_OWNERS
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
diff --git a/SDK_OWNERS b/SDK_OWNERS
new file mode 100644
index 000000000000..e8dd5a3cd977
--- /dev/null
+++ b/SDK_OWNERS
@@ -0,0 +1,5 @@
+amhk@google.com
+kimalexander@google.com
+lus@google.com
+michaelwr@google.com
+paulduffin@google.com
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
index ff4af69fd77c..8bd3ef4f4d1a 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
@@ -121,6 +121,9 @@ public final class QuotaController extends StateController {
private static final String ALARM_TAG_CLEANUP = "*job.cleanup*";
private static final String ALARM_TAG_QUOTA_CHECK = "*job.quota_check*";
+ private static final String TRACE_QUOTA_STATE_CHANGED_TAG = "QuotaStateChanged:";
+ private static final String TRACE_QUOTA_STATE_CHANGED_DELIMITER = "#";
+
private static final int SYSTEM_APP_CHECK_FLAGS =
PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
| PackageManager.GET_PERMISSIONS | PackageManager.MATCH_KNOWN_PACKAGES;
@@ -2657,11 +2660,12 @@ public final class QuotaController extends StateController {
if (timeRemainingMs <= 50) {
// Less than 50 milliseconds left. Start process of shutting down jobs.
if (DEBUG) Slog.d(TAG, pkg + " has reached its quota.");
- if (Trace.isTagEnabled(Trace.TRACE_TAG_SYSTEM_SERVER)) {
- Trace.instantForTrack(Trace.TRACE_TAG_SYSTEM_SERVER,
- JobSchedulerService.TRACE_TRACK_NAME,
- pkg + "#" + MSG_REACHED_TIME_QUOTA);
- }
+ final StringBuilder traceMsg = new StringBuilder();
+ traceMsg.append(TRACE_QUOTA_STATE_CHANGED_TAG)
+ .append(pkg)
+ .append(TRACE_QUOTA_STATE_CHANGED_DELIMITER)
+ .append(MSG_REACHED_TIME_QUOTA);
+ Trace.instant(Trace.TRACE_TAG_POWER, traceMsg.toString());
mStateChangedListener.onControllerStateChanged(
maybeUpdateConstraintForPkgLocked(
sElapsedRealtimeClock.millis(),
@@ -2690,11 +2694,12 @@ public final class QuotaController extends StateController {
pkg.userId, pkg.packageName);
if (timeRemainingMs <= 0) {
if (DEBUG) Slog.d(TAG, pkg + " has reached its EJ quota.");
- if (Trace.isTagEnabled(Trace.TRACE_TAG_SYSTEM_SERVER)) {
- Trace.instantForTrack(Trace.TRACE_TAG_SYSTEM_SERVER,
- JobSchedulerService.TRACE_TRACK_NAME,
- pkg + "#" + MSG_REACHED_EJ_TIME_QUOTA);
- }
+ final StringBuilder traceMsg = new StringBuilder();
+ traceMsg.append(TRACE_QUOTA_STATE_CHANGED_TAG)
+ .append(pkg)
+ .append(TRACE_QUOTA_STATE_CHANGED_DELIMITER)
+ .append(MSG_REACHED_EJ_TIME_QUOTA);
+ Trace.instant(Trace.TRACE_TAG_POWER, traceMsg.toString());
mStateChangedListener.onControllerStateChanged(
maybeUpdateConstraintForPkgLocked(
sElapsedRealtimeClock.millis(),
@@ -2719,11 +2724,12 @@ public final class QuotaController extends StateController {
Slog.d(TAG, pkg + " has reached its count quota.");
}
- if (Trace.isTagEnabled(Trace.TRACE_TAG_SYSTEM_SERVER)) {
- Trace.instantForTrack(Trace.TRACE_TAG_SYSTEM_SERVER,
- JobSchedulerService.TRACE_TRACK_NAME,
- pkg + "#" + MSG_REACHED_COUNT_QUOTA);
- }
+ final StringBuilder traceMsg = new StringBuilder();
+ traceMsg.append(TRACE_QUOTA_STATE_CHANGED_TAG)
+ .append(pkg)
+ .append(TRACE_QUOTA_STATE_CHANGED_DELIMITER)
+ .append(MSG_REACHED_COUNT_QUOTA);
+ Trace.instant(Trace.TRACE_TAG_POWER, traceMsg.toString());
mStateChangedListener.onControllerStateChanged(
maybeUpdateConstraintForPkgLocked(
diff --git a/boot/boot-image-profile-extra.txt b/boot/boot-image-profile-extra.txt
index 11ca1dcc181e..e31eb3a993f0 100644
--- a/boot/boot-image-profile-extra.txt
+++ b/boot/boot-image-profile-extra.txt
@@ -23,3 +23,4 @@ HSPLandroid/graphics/Color;->luminance()F
# For now, compile all methods in MessageQueue to avoid performance cliffs for
# flagged/evolving hot code paths. See: b/338098106
HSPLandroid/os/MessageQueue;->*
+HSPLandroid/os/MessageQueue$*;->*
diff --git a/core/api/current.txt b/core/api/current.txt
index a76c685ce219..39d086a8bd0f 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -294,7 +294,7 @@ package android {
field public static final String SET_ALARM = "com.android.alarm.permission.SET_ALARM";
field public static final String SET_ALWAYS_FINISH = "android.permission.SET_ALWAYS_FINISH";
field public static final String SET_ANIMATION_SCALE = "android.permission.SET_ANIMATION_SCALE";
- field @FlaggedApi("android.hardware.biometrics.custom_biometric_prompt") public static final String SET_BIOMETRIC_DIALOG_ADVANCED = "android.permission.SET_BIOMETRIC_DIALOG_ADVANCED";
+ field public static final String SET_BIOMETRIC_DIALOG_ADVANCED = "android.permission.SET_BIOMETRIC_DIALOG_ADVANCED";
field public static final String SET_DEBUG_APP = "android.permission.SET_DEBUG_APP";
field @Deprecated public static final String SET_PREFERRED_APPLICATIONS = "android.permission.SET_PREFERRED_APPLICATIONS";
field public static final String SET_PROCESS_LIMIT = "android.permission.SET_PROCESS_LIMIT";
@@ -1609,6 +1609,7 @@ package android {
field public static final int summaryColumn = 16843426; // 0x10102a2
field public static final int summaryOff = 16843248; // 0x10101f0
field public static final int summaryOn = 16843247; // 0x10101ef
+ field @FlaggedApi("android.view.accessibility.supplemental_description") public static final int supplementalDescription;
field public static final int supportedTypes = 16844369; // 0x1010651
field public static final int supportsAssist = 16844016; // 0x10104f0
field public static final int supportsBatteryGameMode = 16844374; // 0x1010656
@@ -8783,7 +8784,8 @@ package android.app.appfunctions {
@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 java.util.function.Consumer<android.app.appfunctions.ExecuteAppFunctionResponse>);
- method 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.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
field public static final int APP_FUNCTION_STATE_DISABLED = 2; // 0x2
@@ -8816,6 +8818,7 @@ package android.app.appfunctions {
@FlaggedApi("android.app.appfunctions.flags.enable_app_function_manager") public final class ExecuteAppFunctionResponse implements android.os.Parcelable {
method public int describeContents();
+ method public int getErrorCategory();
method @Nullable public String getErrorMessage();
method @NonNull public android.os.Bundle getExtras();
method public int getResultCode();
@@ -8825,14 +8828,19 @@ package android.app.appfunctions {
method @FlaggedApi("android.app.appfunctions.flags.enable_app_function_manager") @NonNull public static android.app.appfunctions.ExecuteAppFunctionResponse newSuccess(@NonNull android.app.appsearch.GenericDocument, @Nullable android.os.Bundle);
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.app.appfunctions.ExecuteAppFunctionResponse> CREATOR;
+ field public static final int ERROR_CATEGORY_APP = 3; // 0x3
+ field public static final int ERROR_CATEGORY_REQUEST_ERROR = 1; // 0x1
+ field public static final int ERROR_CATEGORY_SYSTEM = 2; // 0x2
+ field public static final int ERROR_CATEGORY_UNKNOWN = 0; // 0x0
field public static final String PROPERTY_RETURN_VALUE = "returnValue";
- field public static final int RESULT_APP_UNKNOWN_ERROR = 2; // 0x2
- field public static final int RESULT_CANCELLED = 6; // 0x6
- field public static final int RESULT_DENIED = 1; // 0x1
- field public static final int RESULT_DISABLED = 5; // 0x5
- field public static final int RESULT_INTERNAL_ERROR = 3; // 0x3
- field public static final int RESULT_INVALID_ARGUMENT = 4; // 0x4
+ field public static final int RESULT_APP_UNKNOWN_ERROR = 3000; // 0xbb8
+ field public static final int RESULT_CANCELLED = 2001; // 0x7d1
+ field public static final int RESULT_DENIED = 1000; // 0x3e8
+ field public static final int RESULT_DISABLED = 1002; // 0x3ea
+ field public static final int RESULT_FUNCTION_NOT_FOUND = 1003; // 0x3eb
+ field public static final int RESULT_INVALID_ARGUMENT = 1001; // 0x3e9
field public static final int RESULT_OK = 0; // 0x0
+ field public static final int RESULT_SYSTEM_ERROR = 2000; // 0x7d0
}
}
@@ -16748,7 +16756,7 @@ package android.graphics {
method public boolean hasGlyph(String);
method public final boolean isAntiAlias();
method public final boolean isDither();
- method public boolean isElegantTextHeight();
+ method @Deprecated @FlaggedApi("com.android.text.flags.deprecate_elegant_text_height_api") public boolean isElegantTextHeight();
method public final boolean isFakeBoldText();
method public final boolean isFilterBitmap();
method public final boolean isLinearText();
@@ -16769,7 +16777,7 @@ package android.graphics {
method public void setColor(@ColorLong long);
method public android.graphics.ColorFilter setColorFilter(android.graphics.ColorFilter);
method public void setDither(boolean);
- method public void setElegantTextHeight(boolean);
+ method @Deprecated @FlaggedApi("com.android.text.flags.deprecate_elegant_text_height_api") public void setElegantTextHeight(boolean);
method public void setEndHyphenEdit(int);
method public void setFakeBoldText(boolean);
method public void setFilterBitmap(boolean);
@@ -17362,6 +17370,25 @@ package android.graphics {
method public boolean setUseCompositingLayer(boolean, @Nullable android.graphics.Paint);
}
+ @FlaggedApi("com.android.graphics.hwui.flags.runtime_color_filters_blenders") public class RuntimeColorFilter extends android.graphics.ColorFilter {
+ ctor public RuntimeColorFilter(@NonNull String);
+ method public void setColorUniform(@NonNull String, @ColorInt int);
+ method public void setColorUniform(@NonNull String, @ColorLong long);
+ method public void setColorUniform(@NonNull String, @NonNull android.graphics.Color);
+ method public void setFloatUniform(@NonNull String, float);
+ method public void setFloatUniform(@NonNull String, float, float);
+ method public void setFloatUniform(@NonNull String, float, float, float);
+ method public void setFloatUniform(@NonNull String, float, float, float, float);
+ method public void setFloatUniform(@NonNull String, @NonNull float[]);
+ method public void setInputColorFilter(@NonNull String, @NonNull android.graphics.ColorFilter);
+ method public void setInputShader(@NonNull String, @NonNull android.graphics.Shader);
+ method public void setIntUniform(@NonNull String, int);
+ method public void setIntUniform(@NonNull String, int, int);
+ method public void setIntUniform(@NonNull String, int, int, int);
+ method public void setIntUniform(@NonNull String, int, int, int, int);
+ method public void setIntUniform(@NonNull String, @NonNull int[]);
+ }
+
public class RuntimeShader extends android.graphics.Shader {
ctor public RuntimeShader(@NonNull String);
method public void setColorUniform(@NonNull String, @ColorInt int);
@@ -19104,11 +19131,11 @@ package android.hardware.biometrics {
method @RequiresPermission(android.Manifest.permission.USE_BIOMETRIC) public void authenticate(@NonNull android.hardware.biometrics.BiometricPrompt.CryptoObject, @NonNull android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull android.hardware.biometrics.BiometricPrompt.AuthenticationCallback);
method @RequiresPermission(android.Manifest.permission.USE_BIOMETRIC) public void authenticate(@NonNull android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull android.hardware.biometrics.BiometricPrompt.AuthenticationCallback);
method @Nullable public int getAllowedAuthenticators();
- method @FlaggedApi("android.hardware.biometrics.custom_biometric_prompt") @Nullable public android.hardware.biometrics.PromptContentView getContentView();
+ method @Nullable public android.hardware.biometrics.PromptContentView getContentView();
method @Nullable public CharSequence getDescription();
- method @FlaggedApi("android.hardware.biometrics.custom_biometric_prompt") @Nullable @RequiresPermission(android.Manifest.permission.SET_BIOMETRIC_DIALOG_ADVANCED) public android.graphics.Bitmap getLogoBitmap();
- method @FlaggedApi("android.hardware.biometrics.custom_biometric_prompt") @Nullable @RequiresPermission(android.Manifest.permission.SET_BIOMETRIC_DIALOG_ADVANCED) public String getLogoDescription();
- method @FlaggedApi("android.hardware.biometrics.custom_biometric_prompt") @DrawableRes @RequiresPermission(android.Manifest.permission.SET_BIOMETRIC_DIALOG_ADVANCED) public int getLogoRes();
+ method @Nullable @RequiresPermission(android.Manifest.permission.SET_BIOMETRIC_DIALOG_ADVANCED) public android.graphics.Bitmap getLogoBitmap();
+ method @Nullable @RequiresPermission(android.Manifest.permission.SET_BIOMETRIC_DIALOG_ADVANCED) public String getLogoDescription();
+ method @DrawableRes @RequiresPermission(android.Manifest.permission.SET_BIOMETRIC_DIALOG_ADVANCED) public int getLogoRes();
method @Nullable public CharSequence getNegativeButtonText();
method @Nullable public CharSequence getSubtitle();
method @NonNull public CharSequence getTitle();
@@ -19157,12 +19184,12 @@ package android.hardware.biometrics {
method @NonNull public android.hardware.biometrics.BiometricPrompt build();
method @NonNull public android.hardware.biometrics.BiometricPrompt.Builder setAllowedAuthenticators(int);
method @NonNull public android.hardware.biometrics.BiometricPrompt.Builder setConfirmationRequired(boolean);
- method @FlaggedApi("android.hardware.biometrics.custom_biometric_prompt") @NonNull public android.hardware.biometrics.BiometricPrompt.Builder setContentView(@NonNull android.hardware.biometrics.PromptContentView);
+ method @NonNull public android.hardware.biometrics.BiometricPrompt.Builder setContentView(@NonNull android.hardware.biometrics.PromptContentView);
method @NonNull public android.hardware.biometrics.BiometricPrompt.Builder setDescription(@NonNull CharSequence);
method @Deprecated @NonNull public android.hardware.biometrics.BiometricPrompt.Builder setDeviceCredentialAllowed(boolean);
- method @FlaggedApi("android.hardware.biometrics.custom_biometric_prompt") @NonNull @RequiresPermission(android.Manifest.permission.SET_BIOMETRIC_DIALOG_ADVANCED) public android.hardware.biometrics.BiometricPrompt.Builder setLogoBitmap(@NonNull android.graphics.Bitmap);
- method @FlaggedApi("android.hardware.biometrics.custom_biometric_prompt") @NonNull @RequiresPermission(android.Manifest.permission.SET_BIOMETRIC_DIALOG_ADVANCED) public android.hardware.biometrics.BiometricPrompt.Builder setLogoDescription(@NonNull String);
- method @FlaggedApi("android.hardware.biometrics.custom_biometric_prompt") @NonNull @RequiresPermission(android.Manifest.permission.SET_BIOMETRIC_DIALOG_ADVANCED) public android.hardware.biometrics.BiometricPrompt.Builder setLogoRes(@DrawableRes int);
+ method @NonNull @RequiresPermission(android.Manifest.permission.SET_BIOMETRIC_DIALOG_ADVANCED) public android.hardware.biometrics.BiometricPrompt.Builder setLogoBitmap(@NonNull android.graphics.Bitmap);
+ method @NonNull @RequiresPermission(android.Manifest.permission.SET_BIOMETRIC_DIALOG_ADVANCED) public android.hardware.biometrics.BiometricPrompt.Builder setLogoDescription(@NonNull String);
+ method @NonNull @RequiresPermission(android.Manifest.permission.SET_BIOMETRIC_DIALOG_ADVANCED) public android.hardware.biometrics.BiometricPrompt.Builder setLogoRes(@DrawableRes int);
method @NonNull public android.hardware.biometrics.BiometricPrompt.Builder setNegativeButton(@NonNull CharSequence, @NonNull java.util.concurrent.Executor, @NonNull android.content.DialogInterface.OnClickListener);
method @NonNull public android.hardware.biometrics.BiometricPrompt.Builder setSubtitle(@NonNull CharSequence);
method @NonNull public android.hardware.biometrics.BiometricPrompt.Builder setTitle(@NonNull CharSequence);
@@ -19185,27 +19212,27 @@ package android.hardware.biometrics {
method @Nullable public java.security.Signature getSignature();
}
- @FlaggedApi("android.hardware.biometrics.custom_biometric_prompt") public interface PromptContentItem {
+ public interface PromptContentItem {
}
- @FlaggedApi("android.hardware.biometrics.custom_biometric_prompt") public final class PromptContentItemBulletedText implements android.os.Parcelable android.hardware.biometrics.PromptContentItem {
+ public final class PromptContentItemBulletedText implements android.os.Parcelable android.hardware.biometrics.PromptContentItem {
ctor public PromptContentItemBulletedText(@NonNull String);
method public int describeContents();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.hardware.biometrics.PromptContentItemBulletedText> CREATOR;
}
- @FlaggedApi("android.hardware.biometrics.custom_biometric_prompt") public final class PromptContentItemPlainText implements android.os.Parcelable android.hardware.biometrics.PromptContentItem {
+ public final class PromptContentItemPlainText implements android.os.Parcelable android.hardware.biometrics.PromptContentItem {
ctor public PromptContentItemPlainText(@NonNull String);
method public int describeContents();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.hardware.biometrics.PromptContentItemPlainText> CREATOR;
}
- @FlaggedApi("android.hardware.biometrics.custom_biometric_prompt") public interface PromptContentView {
+ public interface PromptContentView {
}
- @FlaggedApi("android.hardware.biometrics.custom_biometric_prompt") public final class PromptContentViewWithMoreOptionsButton implements android.os.Parcelable android.hardware.biometrics.PromptContentView {
+ public final class PromptContentViewWithMoreOptionsButton implements android.os.Parcelable android.hardware.biometrics.PromptContentView {
method public int describeContents();
method @Nullable @RequiresPermission(android.Manifest.permission.SET_BIOMETRIC_DIALOG_ADVANCED) public String getDescription();
method @NonNull @RequiresPermission(android.Manifest.permission.SET_BIOMETRIC_DIALOG_ADVANCED) public android.content.DialogInterface.OnClickListener getMoreOptionsButtonListener();
@@ -19220,7 +19247,7 @@ package android.hardware.biometrics {
method @NonNull @RequiresPermission(android.Manifest.permission.SET_BIOMETRIC_DIALOG_ADVANCED) public android.hardware.biometrics.PromptContentViewWithMoreOptionsButton.Builder setMoreOptionsButtonListener(@NonNull java.util.concurrent.Executor, @NonNull android.content.DialogInterface.OnClickListener);
}
- @FlaggedApi("android.hardware.biometrics.custom_biometric_prompt") public final class PromptVerticalListContentView implements android.os.Parcelable android.hardware.biometrics.PromptContentView {
+ public final class PromptVerticalListContentView implements android.os.Parcelable android.hardware.biometrics.PromptContentView {
method public int describeContents();
method @Nullable public String getDescription();
method @NonNull public java.util.List<android.hardware.biometrics.PromptContentItem> getListItems();
@@ -19315,6 +19342,8 @@ package android.hardware.camera2 {
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> AUTOMOTIVE_LENS_FACING;
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> AUTOMOTIVE_LOCATION;
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> COLOR_CORRECTION_AVAILABLE_ABERRATION_MODES;
+ field @FlaggedApi("com.android.internal.camera.flags.color_temperature") @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> COLOR_CORRECTION_AVAILABLE_MODES;
+ field @FlaggedApi("com.android.internal.camera.flags.color_temperature") @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.util.Range<java.lang.Integer>> COLOR_CORRECTION_COLOR_TEMPERATURE_RANGE;
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> CONTROL_AE_AVAILABLE_ANTIBANDING_MODES;
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> CONTROL_AE_AVAILABLE_MODES;
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.util.Range<java.lang.Integer>[]> CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES;
@@ -19616,6 +19645,7 @@ package android.hardware.camera2 {
field public static final int COLOR_CORRECTION_ABERRATION_MODE_FAST = 1; // 0x1
field public static final int COLOR_CORRECTION_ABERRATION_MODE_HIGH_QUALITY = 2; // 0x2
field public static final int COLOR_CORRECTION_ABERRATION_MODE_OFF = 0; // 0x0
+ field @FlaggedApi("com.android.internal.camera.flags.color_temperature") public static final int COLOR_CORRECTION_MODE_CCT = 3; // 0x3
field public static final int COLOR_CORRECTION_MODE_FAST = 1; // 0x1
field public static final int COLOR_CORRECTION_MODE_HIGH_QUALITY = 2; // 0x2
field public static final int COLOR_CORRECTION_MODE_TRANSFORM_MATRIX = 0; // 0x0
@@ -19902,6 +19932,8 @@ package android.hardware.camera2 {
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Boolean> BLACK_LEVEL_LOCK;
field @NonNull public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Integer> COLOR_CORRECTION_ABERRATION_MODE;
+ field @FlaggedApi("com.android.internal.camera.flags.color_temperature") @NonNull public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Integer> COLOR_CORRECTION_COLOR_TEMPERATURE;
+ field @FlaggedApi("com.android.internal.camera.flags.color_temperature") @NonNull public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Integer> COLOR_CORRECTION_COLOR_TINT;
field @NonNull public static final android.hardware.camera2.CaptureRequest.Key<android.hardware.camera2.params.RggbChannelVector> COLOR_CORRECTION_GAINS;
field @NonNull public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Integer> COLOR_CORRECTION_MODE;
field @NonNull public static final android.hardware.camera2.CaptureRequest.Key<android.hardware.camera2.params.ColorSpaceTransform> COLOR_CORRECTION_TRANSFORM;
@@ -19992,6 +20024,8 @@ package android.hardware.camera2 {
method public int getSequenceId();
field @NonNull public static final android.hardware.camera2.CaptureResult.Key<java.lang.Boolean> BLACK_LEVEL_LOCK;
field @NonNull public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> COLOR_CORRECTION_ABERRATION_MODE;
+ field @FlaggedApi("com.android.internal.camera.flags.color_temperature") @NonNull public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> COLOR_CORRECTION_COLOR_TEMPERATURE;
+ field @FlaggedApi("com.android.internal.camera.flags.color_temperature") @NonNull public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> COLOR_CORRECTION_COLOR_TINT;
field @NonNull public static final android.hardware.camera2.CaptureResult.Key<android.hardware.camera2.params.RggbChannelVector> COLOR_CORRECTION_GAINS;
field @NonNull public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> COLOR_CORRECTION_MODE;
field @NonNull public static final android.hardware.camera2.CaptureResult.Key<android.hardware.camera2.params.ColorSpaceTransform> COLOR_CORRECTION_TRANSFORM;
@@ -21373,6 +21407,7 @@ package android.media {
field public static final int TYPE_IP = 20; // 0x14
field public static final int TYPE_LINE_ANALOG = 5; // 0x5
field public static final int TYPE_LINE_DIGITAL = 6; // 0x6
+ field @FlaggedApi("android.media.audio.enable_multichannel_group_device") public static final int TYPE_MULTICHANNEL_GROUP = 32; // 0x20
field public static final int TYPE_REMOTE_SUBMIX = 25; // 0x19
field public static final int TYPE_TELEPHONY = 18; // 0x12
field public static final int TYPE_TV_TUNER = 17; // 0x11
@@ -23090,6 +23125,65 @@ package android.media {
field public static final int AC4Profile11 = 514; // 0x202
field public static final int AC4Profile21 = 1026; // 0x402
field public static final int AC4Profile22 = 1028; // 0x404
+ field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel11Band0 = 513; // 0x201
+ field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel11Band1 = 514; // 0x202
+ field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel11Band2 = 516; // 0x204
+ field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel11Band3 = 520; // 0x208
+ field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel1Band0 = 257; // 0x101
+ field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel1Band1 = 258; // 0x102
+ field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel1Band2 = 260; // 0x104
+ field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel1Band3 = 264; // 0x108
+ field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel21Band0 = 2049; // 0x801
+ field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel21Band1 = 2050; // 0x802
+ field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel21Band2 = 2052; // 0x804
+ field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel21Band3 = 2056; // 0x808
+ field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel2Band0 = 1025; // 0x401
+ field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel2Band1 = 1026; // 0x402
+ field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel2Band2 = 1028; // 0x404
+ field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel2Band3 = 1032; // 0x408
+ field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel31Band0 = 8193; // 0x2001
+ field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel31Band1 = 8194; // 0x2002
+ field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel31Band2 = 8196; // 0x2004
+ field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel31Band3 = 8200; // 0x2008
+ field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel3Band0 = 4097; // 0x1001
+ field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel3Band1 = 4098; // 0x1002
+ field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel3Band2 = 4100; // 0x1004
+ field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel3Band3 = 4104; // 0x1008
+ field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel41Band0 = 32769; // 0x8001
+ field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel41Band1 = 32770; // 0x8002
+ field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel41Band2 = 32772; // 0x8004
+ field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel41Band3 = 32776; // 0x8008
+ field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel4Band0 = 16385; // 0x4001
+ field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel4Band1 = 16386; // 0x4002
+ field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel4Band2 = 16388; // 0x4004
+ field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel4Band3 = 16392; // 0x4008
+ field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel51Band0 = 131073; // 0x20001
+ field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel51Band1 = 131074; // 0x20002
+ field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel51Band2 = 131076; // 0x20004
+ field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel51Band3 = 131080; // 0x20008
+ field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel5Band0 = 65537; // 0x10001
+ field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel5Band1 = 65538; // 0x10002
+ field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel5Band2 = 65540; // 0x10004
+ field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel5Band3 = 65544; // 0x10008
+ field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel61Band0 = 524289; // 0x80001
+ field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel61Band1 = 524290; // 0x80002
+ field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel61Band2 = 524292; // 0x80004
+ field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel61Band3 = 524296; // 0x80008
+ field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel6Band0 = 262145; // 0x40001
+ field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel6Band1 = 262146; // 0x40002
+ field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel6Band2 = 262148; // 0x40004
+ field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel6Band3 = 262152; // 0x40008
+ field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel71Band0 = 2097153; // 0x200001
+ field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel71Band1 = 2097154; // 0x200002
+ field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel71Band2 = 2097156; // 0x200004
+ field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel71Band3 = 2097160; // 0x200008
+ field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel7Band0 = 1048577; // 0x100001
+ field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel7Band1 = 1048578; // 0x100002
+ field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel7Band2 = 1048580; // 0x100004
+ field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel7Band3 = 1048584; // 0x100008
+ field @FlaggedApi("android.media.codec.apv_support") public static final int APVProfile422_10 = 1; // 0x1
+ field @FlaggedApi("android.media.codec.apv_support") public static final int APVProfile422_10HDR10 = 4096; // 0x1000
+ field @FlaggedApi("android.media.codec.apv_support") public static final int APVProfile422_10HDR10Plus = 8192; // 0x2000
field public static final int AV1Level2 = 1; // 0x1
field public static final int AV1Level21 = 2; // 0x2
field public static final int AV1Level22 = 4; // 0x4
@@ -23940,6 +24034,7 @@ package android.media {
field public static final String MIMETYPE_TEXT_CEA_708 = "text/cea-708";
field public static final String MIMETYPE_TEXT_SUBRIP = "application/x-subrip";
field public static final String MIMETYPE_TEXT_VTT = "text/vtt";
+ field @FlaggedApi("android.media.codec.apv_support") public static final String MIMETYPE_VIDEO_APV = "video/apv";
field public static final String MIMETYPE_VIDEO_AV1 = "video/av01";
field public static final String MIMETYPE_VIDEO_AVC = "video/avc";
field public static final String MIMETYPE_VIDEO_DOLBY_VISION = "video/dolby-vision";
@@ -34180,6 +34275,7 @@ package android.os {
method public android.os.StrictMode.VmPolicy build();
method @NonNull public android.os.StrictMode.VmPolicy.Builder detectActivityLeaks();
method @NonNull public android.os.StrictMode.VmPolicy.Builder detectAll();
+ method @FlaggedApi("com.android.window.flags.bal_strict_mode_ro") @NonNull public android.os.StrictMode.VmPolicy.Builder detectBlockedBackgroundActivityLaunch();
method @NonNull public android.os.StrictMode.VmPolicy.Builder detectCleartextNetwork();
method @NonNull public android.os.StrictMode.VmPolicy.Builder detectContentUriWithoutPermission();
method @NonNull public android.os.StrictMode.VmPolicy.Builder detectCredentialProtectedWhileLocked();
@@ -34192,6 +34288,7 @@ package android.os {
method @NonNull public android.os.StrictMode.VmPolicy.Builder detectNonSdkApiUsage();
method @NonNull public android.os.StrictMode.VmPolicy.Builder detectUnsafeIntentLaunch();
method @NonNull public android.os.StrictMode.VmPolicy.Builder detectUntaggedSockets();
+ method @FlaggedApi("com.android.window.flags.bal_strict_mode_ro") @NonNull public android.os.StrictMode.VmPolicy.Builder ignoreBlockedBackgroundActivityLaunch();
method @NonNull public android.os.StrictMode.VmPolicy.Builder penaltyDeath();
method @NonNull public android.os.StrictMode.VmPolicy.Builder penaltyDeathOnCleartextNetwork();
method @NonNull public android.os.StrictMode.VmPolicy.Builder penaltyDeathOnFileUriExposure();
@@ -39814,7 +39911,7 @@ package android.security {
package android.security.advancedprotection {
- @FlaggedApi("android.security.aapm_api") public class AdvancedProtectionManager {
+ @FlaggedApi("android.security.aapm_api") public final class AdvancedProtectionManager {
method @RequiresPermission(android.Manifest.permission.QUERY_ADVANCED_PROTECTION_MODE) public boolean isAdvancedProtectionEnabled();
method @RequiresPermission(android.Manifest.permission.QUERY_ADVANCED_PROTECTION_MODE) public void registerAdvancedProtectionCallback(@NonNull java.util.concurrent.Executor, @NonNull android.security.advancedprotection.AdvancedProtectionManager.Callback);
method @RequiresPermission(android.Manifest.permission.QUERY_ADVANCED_PROTECTION_MODE) public void unregisterAdvancedProtectionCallback(@NonNull android.security.advancedprotection.AdvancedProtectionManager.Callback);
@@ -41480,6 +41577,7 @@ package android.service.notification {
method public final void cancelNotification(String);
method public final void cancelNotifications(String[]);
method public final void clearRequestedListenerHints();
+ method @FlaggedApi("android.service.notification.notification_conversation_channel_management") @Nullable public final android.app.NotificationChannel createConversationNotificationChannelForPackage(@NonNull String, @NonNull android.os.UserHandle, @NonNull String, @NonNull String);
method public android.service.notification.StatusBarNotification[] getActiveNotifications();
method public android.service.notification.StatusBarNotification[] getActiveNotifications(String[]);
method public final int getCurrentInterruptionFilter();
@@ -53140,6 +53238,7 @@ package android.view {
method public android.animation.StateListAnimator getStateListAnimator();
method protected int getSuggestedMinimumHeight();
method protected int getSuggestedMinimumWidth();
+ method @FlaggedApi("android.view.accessibility.supplemental_description") @Nullable public CharSequence getSupplementalDescription();
method @NonNull public java.util.List<android.graphics.Rect> getSystemGestureExclusionRects();
method @Deprecated public int getSystemUiVisibility();
method public Object getTag();
@@ -53520,6 +53619,7 @@ package android.view {
method public void setSoundEffectsEnabled(boolean);
method public void setStateDescription(@Nullable CharSequence);
method public void setStateListAnimator(android.animation.StateListAnimator);
+ method @FlaggedApi("android.view.accessibility.supplemental_description") public void setSupplementalDescription(@Nullable CharSequence);
method public void setSystemGestureExclusionRects(@NonNull java.util.List<android.graphics.Rect>);
method @Deprecated public void setSystemUiVisibility(int);
method public void setTag(Object);
@@ -55126,6 +55226,7 @@ package android.view.accessibility {
field public static final int CONTENT_CHANGE_TYPE_PANE_TITLE = 8; // 0x8
field public static final int CONTENT_CHANGE_TYPE_STATE_DESCRIPTION = 64; // 0x40
field public static final int CONTENT_CHANGE_TYPE_SUBTREE = 1; // 0x1
+ field @FlaggedApi("android.view.accessibility.supplemental_description") public static final int CONTENT_CHANGE_TYPE_SUPPLEMENTAL_DESCRIPTION = 32768; // 0x8000
field public static final int CONTENT_CHANGE_TYPE_TEXT = 2; // 0x2
field public static final int CONTENT_CHANGE_TYPE_UNDEFINED = 0; // 0x0
field @NonNull public static final android.os.Parcelable.Creator<android.view.accessibility.AccessibilityEvent> CREATOR;
@@ -55288,6 +55389,7 @@ package android.view.accessibility {
method @Nullable public android.view.accessibility.AccessibilityNodeInfo getParent(int);
method public android.view.accessibility.AccessibilityNodeInfo.RangeInfo getRangeInfo();
method @Nullable public CharSequence getStateDescription();
+ method @FlaggedApi("android.view.accessibility.supplemental_description") @Nullable public CharSequence getSupplementalDescription();
method public CharSequence getText();
method public int getTextSelectionEnd();
method public int getTextSelectionStart();
@@ -55396,6 +55498,7 @@ package android.view.accessibility {
method public void setSource(android.view.View);
method public void setSource(android.view.View, int);
method public void setStateDescription(@Nullable CharSequence);
+ method @FlaggedApi("android.view.accessibility.supplemental_description") public void setSupplementalDescription(@Nullable CharSequence);
method public void setText(CharSequence);
method public void setTextEntryKey(boolean);
method public void setTextSelectable(boolean);
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index 4c9ce07664db..bc73220cb4c1 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -102,6 +102,7 @@ package android.content {
method @NonNull public android.os.UserHandle getUser();
field public static final String PAC_PROXY_SERVICE = "pac_proxy";
field public static final String TEST_NETWORK_SERVICE = "test_network";
+ field @FlaggedApi("android.os.mainline_vcn_platform_api") public static final String VCN_MANAGEMENT_SERVICE = "vcn_management";
field @FlaggedApi("android.webkit.update_service_ipc_wrapper") public static final String WEBVIEW_UPDATE_SERVICE = "webviewupdate";
}
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 5453c2ea3423..e4d55896744c 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -1047,6 +1047,7 @@ package android.app {
method public int getUserLockedFields();
method public boolean isDeleted();
method public void populateFromXml(org.xmlpull.v1.XmlPullParser);
+ method @FlaggedApi("android.service.notification.notification_conversation_channel_management") public void setImportantConversation(boolean);
method public org.json.JSONObject toJson() throws org.json.JSONException;
method public void writeXml(org.xmlpull.v1.XmlSerializer) throws java.io.IOException;
field public static final int USER_LOCKED_SOUND = 32; // 0x20
@@ -5286,11 +5287,13 @@ package android.hardware.display {
public final class VirtualDisplayConfig implements android.os.Parcelable {
method @FlaggedApi("android.companion.virtualdevice.flags.virtual_display_insets") @Nullable public android.view.DisplayCutout getDisplayCutout();
method @FlaggedApi("android.companion.virtual.flags.vdm_custom_home") public boolean isHomeSupported();
+ method @FlaggedApi("com.android.window.flags.vdm_force_app_universal_resizable_api") public boolean isIgnoreActivitySizeRestrictions();
}
public static final class VirtualDisplayConfig.Builder {
method @FlaggedApi("android.companion.virtualdevice.flags.virtual_display_insets") @NonNull public android.hardware.display.VirtualDisplayConfig.Builder setDisplayCutout(@Nullable android.view.DisplayCutout);
method @FlaggedApi("android.companion.virtual.flags.vdm_custom_home") @NonNull public android.hardware.display.VirtualDisplayConfig.Builder setHomeSupported(boolean);
+ method @FlaggedApi("com.android.window.flags.vdm_force_app_universal_resizable_api") @NonNull public android.hardware.display.VirtualDisplayConfig.Builder setIgnoreActivitySizeRestrictions(boolean);
}
}
@@ -7227,7 +7230,6 @@ package android.media {
field @RequiresPermission(allOf={android.Manifest.permission.MODIFY_PHONE_STATE, android.Manifest.permission.MODIFY_AUDIO_ROUTING}) public static final int USAGE_CALL_ASSISTANT = 17; // 0x11
field @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public static final int USAGE_EMERGENCY = 1000; // 0x3e8
field @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public static final int USAGE_SAFETY = 1001; // 0x3e9
- field @FlaggedApi("android.media.audio.speaker_cleanup_usage") @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public static final int USAGE_SPEAKER_CLEANUP = 1004; // 0x3ec
field @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public static final int USAGE_VEHICLE_STATUS = 1002; // 0x3ea
}
@@ -7602,7 +7604,7 @@ package android.media {
public final class MediaRecorder.AudioSource {
field @RequiresPermission(android.Manifest.permission.CAPTURE_AUDIO_OUTPUT) public static final int ECHO_REFERENCE = 1997; // 0x7cd
field @RequiresPermission(android.Manifest.permission.CAPTURE_AUDIO_HOTWORD) public static final int HOTWORD = 1999; // 0x7cf
- field @RequiresPermission(android.Manifest.permission.CAPTURE_AUDIO_OUTPUT) public static final int RADIO_TUNER = 1998; // 0x7ce
+ field @RequiresPermission(android.Manifest.permission.CAPTURE_TUNER_AUDIO_INPUT) public static final int RADIO_TUNER = 1998; // 0x7ce
field @RequiresPermission(android.Manifest.permission.ACCESS_ULTRASOUND) public static final int ULTRASOUND = 2000; // 0x7d0
}
@@ -12464,7 +12466,16 @@ package android.security {
package android.security.advancedprotection {
- @FlaggedApi("android.security.aapm_api") public class AdvancedProtectionManager {
+ @FlaggedApi("android.security.aapm_api") public final class AdvancedProtectionFeature implements android.os.Parcelable {
+ ctor public AdvancedProtectionFeature(@NonNull String);
+ method public int describeContents();
+ method @NonNull public String getId();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.security.advancedprotection.AdvancedProtectionFeature> CREATOR;
+ }
+
+ @FlaggedApi("android.security.aapm_api") public final class AdvancedProtectionManager {
+ method @NonNull @RequiresPermission(android.Manifest.permission.SET_ADVANCED_PROTECTION_MODE) public java.util.List<android.security.advancedprotection.AdvancedProtectionFeature> getAdvancedProtectionFeatures();
method @RequiresPermission(android.Manifest.permission.SET_ADVANCED_PROTECTION_MODE) public void setAdvancedProtectionEnabled(boolean);
}
@@ -15702,6 +15713,9 @@ package android.telephony {
method @FlaggedApi("android.permission.flags.get_emergency_role_holder_api_enabled") @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getEmergencyAssistancePackageName();
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean getEmergencyCallbackMode();
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getEmergencyNumberDbVersion();
+ method @FlaggedApi("com.android.internal.telephony.flags.support_isim_record") @NonNull @RequiresPermission(value=android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, conditional=true) public java.util.List<java.lang.String> getImsPcscfAddresses();
+ method @FlaggedApi("com.android.internal.telephony.flags.support_isim_record") @Nullable @RequiresPermission(android.Manifest.permission.USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER) public String getImsPrivateUserIdentity();
+ method @FlaggedApi("com.android.internal.telephony.flags.support_isim_record") @NonNull @RequiresPermission(value=android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, conditional=true) public java.util.List<android.net.Uri> getImsPublicUserIdentities();
method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getIsimDomain();
method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getIsimIst();
method @FlaggedApi("com.android.server.telecom.flags.get_last_known_cell_identity") @Nullable @RequiresPermission(allOf={android.Manifest.permission.ACCESS_FINE_LOCATION, android.Manifest.permission.ACCESS_LAST_KNOWN_CELL_ID}) public android.telephony.CellIdentity getLastKnownCellIdentity();
@@ -15720,6 +15734,7 @@ package android.telephony {
method @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getSimCardState(int);
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getSimCardState(int, int);
method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public java.util.Locale getSimLocale();
+ method @FlaggedApi("com.android.internal.telephony.flags.support_isim_record") @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void getSimServiceTable(int, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<byte[],java.lang.Exception>);
method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public java.util.Collection<android.telephony.UiccSlotMapping> getSimSlotMapping();
method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public java.util.List<android.telephony.RadioAccessSpecifier> getSystemSelectionChannels();
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public java.util.List<android.telephony.TelephonyHistogram> getTelephonyHistograms();
@@ -16844,8 +16859,11 @@ package android.telephony.ims {
method public void callSessionRttMessageReceived(String);
method public void callSessionRttModifyRequestReceived(android.telephony.ims.ImsCallProfile);
method public void callSessionRttModifyResponseReceived(int);
+ method @FlaggedApi("com.android.internal.telephony.flags.support_ims_mmtel_interface") public final void callSessionSendAnbrQuery(int, int, @IntRange(from=0) int);
method public void callSessionSuppServiceReceived(android.telephony.ims.ImsSuppServiceNotification);
method public void callSessionTerminated(android.telephony.ims.ImsReasonInfo);
+ method @FlaggedApi("com.android.internal.telephony.flags.support_ims_mmtel_interface") public void callSessionTransferFailed(@NonNull android.telephony.ims.ImsReasonInfo);
+ method @FlaggedApi("com.android.internal.telephony.flags.support_ims_mmtel_interface") public void callSessionTransferred();
method public void callSessionTtyModeReceived(int);
method public void callSessionUpdateFailed(android.telephony.ims.ImsReasonInfo);
method public void callSessionUpdateReceived(android.telephony.ims.ImsCallProfile);
@@ -17650,6 +17668,26 @@ package android.telephony.ims.feature {
method public int getRadioTech();
}
+ @FlaggedApi("com.android.internal.telephony.flags.support_ims_mmtel_interface") public final class ConnectionFailureInfo implements android.os.Parcelable {
+ method public int describeContents();
+ method public int getCauseCode();
+ method public int getReason();
+ method public int getWaitTimeMillis();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.feature.ConnectionFailureInfo> CREATOR;
+ field public static final int REASON_ACCESS_DENIED = 1; // 0x1
+ field public static final int REASON_NAS_FAILURE = 2; // 0x2
+ field public static final int REASON_NONE = 0; // 0x0
+ field public static final int REASON_NO_SERVICE = 7; // 0x7
+ field public static final int REASON_PDN_NOT_AVAILABLE = 8; // 0x8
+ field public static final int REASON_RACH_FAILURE = 3; // 0x3
+ field public static final int REASON_RF_BUSY = 9; // 0x9
+ field public static final int REASON_RLC_FAILURE = 4; // 0x4
+ field public static final int REASON_RRC_REJECT = 5; // 0x5
+ field public static final int REASON_RRC_TIMEOUT = 6; // 0x6
+ field public static final int REASON_UNSPECIFIED = 65535; // 0xffff
+ }
+
public abstract class ImsFeature {
ctor public ImsFeature();
method public abstract void changeEnabledCapabilities(android.telephony.ims.feature.CapabilityChangeRequest, android.telephony.ims.feature.ImsFeature.CapabilityCallbackProxy);
@@ -17676,6 +17714,11 @@ package android.telephony.ims.feature {
method public void onChangeCapabilityConfigurationError(int, int, int);
}
+ @FlaggedApi("com.android.internal.telephony.flags.support_ims_mmtel_interface") public interface ImsTrafficSessionCallback {
+ method public void onError(@NonNull android.telephony.ims.feature.ConnectionFailureInfo);
+ method public void onReady();
+ }
+
public class MmTelFeature extends android.telephony.ims.feature.ImsFeature {
ctor public MmTelFeature();
ctor public MmTelFeature(@NonNull java.util.concurrent.Executor);
@@ -17688,6 +17731,7 @@ package android.telephony.ims.feature {
method @NonNull public android.telephony.ims.stub.ImsMultiEndpointImplBase getMultiEndpoint();
method @NonNull public android.telephony.ims.stub.ImsSmsImplBase getSmsImplementation();
method @NonNull public android.telephony.ims.stub.ImsUtImplBase getUt();
+ method @FlaggedApi("com.android.internal.telephony.flags.support_ims_mmtel_interface") public final void modifyImsTrafficSession(int, @NonNull android.telephony.ims.feature.ImsTrafficSessionCallback);
method public final void notifyCapabilitiesStatusChanged(@NonNull android.telephony.ims.feature.MmTelFeature.MmTelCapabilities);
method @Deprecated public final void notifyIncomingCall(@NonNull android.telephony.ims.stub.ImsCallSessionImplBase, @NonNull android.os.Bundle);
method @Nullable public final android.telephony.ims.ImsCallSessionListener notifyIncomingCall(@NonNull android.telephony.ims.stub.ImsCallSessionImplBase, @NonNull String, @NonNull android.os.Bundle);
@@ -17708,10 +17752,24 @@ package android.telephony.ims.feature {
method public void setTerminalBasedCallWaitingStatus(boolean);
method public void setUiTtyMode(int, @Nullable android.os.Message);
method public int shouldProcessCall(@NonNull String[]);
+ method @FlaggedApi("com.android.internal.telephony.flags.support_ims_mmtel_interface") public final void startImsTrafficSession(int, int, int, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.feature.ImsTrafficSessionCallback);
+ method @FlaggedApi("com.android.internal.telephony.flags.support_ims_mmtel_interface") public final void stopImsTrafficSession(@NonNull android.telephony.ims.feature.ImsTrafficSessionCallback);
+ method @FlaggedApi("com.android.internal.telephony.flags.support_ims_mmtel_interface") public final void triggerEpsFallback(int);
field public static final int AUDIO_HANDLER_ANDROID = 0; // 0x0
field public static final int AUDIO_HANDLER_BASEBAND = 1; // 0x1
+ field @FlaggedApi("com.android.internal.telephony.flags.support_ims_mmtel_interface") public static final int EPS_FALLBACK_REASON_NO_NETWORK_RESPONSE = 2; // 0x2
+ field @FlaggedApi("com.android.internal.telephony.flags.support_ims_mmtel_interface") public static final int EPS_FALLBACK_REASON_NO_NETWORK_TRIGGER = 1; // 0x1
field public static final String EXTRA_IS_UNKNOWN_CALL = "android.telephony.ims.feature.extra.IS_UNKNOWN_CALL";
field public static final String EXTRA_IS_USSD = "android.telephony.ims.feature.extra.IS_USSD";
+ field @FlaggedApi("com.android.internal.telephony.flags.support_ims_mmtel_interface") public static final int IMS_TRAFFIC_DIRECTION_INCOMING = 0; // 0x0
+ field @FlaggedApi("com.android.internal.telephony.flags.support_ims_mmtel_interface") public static final int IMS_TRAFFIC_DIRECTION_OUTGOING = 1; // 0x1
+ field @FlaggedApi("com.android.internal.telephony.flags.support_ims_mmtel_interface") public static final int IMS_TRAFFIC_TYPE_EMERGENCY = 0; // 0x0
+ field @FlaggedApi("com.android.internal.telephony.flags.support_ims_mmtel_interface") public static final int IMS_TRAFFIC_TYPE_EMERGENCY_SMS = 1; // 0x1
+ field @FlaggedApi("com.android.internal.telephony.flags.support_ims_mmtel_interface") public static final int IMS_TRAFFIC_TYPE_REGISTRATION = 5; // 0x5
+ field @FlaggedApi("com.android.internal.telephony.flags.support_ims_mmtel_interface") public static final int IMS_TRAFFIC_TYPE_SMS = 4; // 0x4
+ field @FlaggedApi("com.android.internal.telephony.flags.support_ims_mmtel_interface") public static final int IMS_TRAFFIC_TYPE_UT_XCAP = 6; // 0x6
+ field @FlaggedApi("com.android.internal.telephony.flags.support_ims_mmtel_interface") public static final int IMS_TRAFFIC_TYPE_VIDEO = 3; // 0x3
+ field @FlaggedApi("com.android.internal.telephony.flags.support_ims_mmtel_interface") public static final int IMS_TRAFFIC_TYPE_VOICE = 2; // 0x2
field public static final int PROCESS_CALL_CSFB = 1; // 0x1
field public static final int PROCESS_CALL_IMS = 0; // 0x0
}
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index a0c4fdb90bf1..98d6f58e6bcf 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -369,6 +369,7 @@ package android.app {
}
public final class NotificationChannel implements android.os.Parcelable {
+ method @FlaggedApi("android.service.notification.notification_conversation_channel_management") @NonNull public android.app.NotificationChannel copy();
method public int getOriginalImportance();
method public boolean isImportanceLockedByCriticalDeviceFunction();
method public void lockFields(int);
@@ -376,7 +377,7 @@ package android.app {
method public void setDeletedTimeMs(long);
method public void setDemoted(boolean);
method public void setImportanceLockedByCriticalDeviceFunction(boolean);
- method public void setImportantConversation(boolean);
+ method @FlaggedApi("android.service.notification.notification_conversation_channel_management") public void setImportantConversation(boolean);
method public void setOriginalImportance(int);
method public void setUserVisibleTaskShown(boolean);
field @FlaggedApi("android.service.notification.notification_classification") public static final String NEWS_ID = "android.app.news";
@@ -1029,6 +1030,7 @@ package android.content {
public abstract class Context {
method @NonNull public java.io.File getCrateDir(@NonNull String);
method public abstract int getDisplayId();
+ method @NonNull public java.util.List<android.content.IntentFilter> getRegisteredIntentFilters(@NonNull android.content.BroadcastReceiver);
method @NonNull public android.os.UserHandle getUser();
method public int getUserId();
method public void setAutofillOptions(@Nullable android.content.AutofillOptions);
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index ecef0db46bb2..2cf718ef364f 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -1962,6 +1962,24 @@ class ContextImpl extends Context {
}
}
+ @Override
+ @NonNull
+ public List<IntentFilter> getRegisteredIntentFilters(@NonNull BroadcastReceiver receiver) {
+ if (mPackageInfo != null) {
+ IIntentReceiver rd = mPackageInfo.findRegisteredReceiverDispatcher(
+ receiver, getOuterContext());
+ try {
+ final List<IntentFilter> filters = ActivityManager.getService()
+ .getRegisteredIntentFilters(rd);
+ return filters == null ? new ArrayList<>() : filters;
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ } else {
+ throw new RuntimeException("Not supported in system context");
+ }
+ }
+
private void validateServiceIntent(Intent service) {
if (service.getComponent() == null && service.getPackage() == null) {
if (getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.LOLLIPOP) {
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index f880901429f7..34a3ad19b270 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -181,6 +181,7 @@ interface IActivityManager {
in IntentFilter filter, in String requiredPermission, int userId, int flags);
@UnsupportedAppUsage
void unregisterReceiver(in IIntentReceiver receiver);
+ List<IntentFilter> getRegisteredIntentFilters(in IIntentReceiver receiver);
/** @deprecated Use {@link #broadcastIntentWithFeature} instead */
@UnsupportedAppUsage(maxTargetSdk=29, publicAlternatives="Use {@link android.content.Context#sendBroadcast(android.content.Intent)} instead")
int broadcastIntent(in IApplicationThread caller, in Intent intent,
diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl
index 0a05144254ab..003104ab2b0b 100644
--- a/core/java/android/app/IActivityTaskManager.aidl
+++ b/core/java/android/app/IActivityTaskManager.aidl
@@ -359,6 +359,23 @@ interface IActivityTaskManager {
in RemoteCallback navigationObserver, in BackAnimationAdapter adaptor);
/**
+ * registers a callback to be invoked when a background activity launch is aborted.
+ *
+ * @param observer callback to be registered.
+ * @return true if the callback was successfully registered, false otherwise.
+ * @hide
+ */
+ boolean registerBackgroundActivityStartCallback(in IBinder binder);
+
+ /**
+ * unregisters a callback to be invoked when a background activity launch is aborted.
+ *
+ * @param observer callback to be registered.
+ * @hide
+ */
+ void unregisterBackgroundActivityStartCallback(in IBinder binder);
+
+ /**
* registers a callback to be invoked when the screen is captured.
*
* @param observer callback to be registered.
diff --git a/core/java/android/app/IBackgroundActivityLaunchCallback.aidl b/core/java/android/app/IBackgroundActivityLaunchCallback.aidl
new file mode 100644
index 000000000000..6dfb5182486a
--- /dev/null
+++ b/core/java/android/app/IBackgroundActivityLaunchCallback.aidl
@@ -0,0 +1,26 @@
+/*
+* 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;
+
+/**
+ * Callback to find out when a background activity launch is aborted.
+ * @hide
+ */
+oneway interface IBackgroundActivityLaunchCallback
+{
+ void onBackgroundActivityLaunchAborted(in String message);
+}
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index a7597b4d566d..a97fa18a3599 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -175,6 +175,7 @@ interface INotificationManager
void setOnNotificationPostedTrimFromListener(in INotificationListener token, int trim);
void setInterruptionFilter(String pkg, int interruptionFilter, boolean fromUser);
+ NotificationChannel createConversationNotificationChannelForPackageFromPrivilegedListener(in INotificationListener token, String pkg, in UserHandle user, String parentChannelId, String conversationId);
void updateNotificationChannelGroupFromPrivilegedListener(in INotificationListener token, String pkg, in UserHandle user, in NotificationChannelGroup group);
void updateNotificationChannelFromPrivilegedListener(in INotificationListener token, String pkg, in UserHandle user, in NotificationChannel channel);
ParceledListSlice getNotificationChannelsFromPrivilegedListener(in INotificationListener token, String pkg, in UserHandle user);
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index 1e45d6fd1674..b8233bc498b8 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -1627,6 +1627,18 @@ public final class LoadedApk {
}
}
+ IIntentReceiver findRegisteredReceiverDispatcher(BroadcastReceiver r, Context context) {
+ synchronized (mReceivers) {
+ final ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher> map =
+ mReceivers.get(context);
+ if (map != null) {
+ final LoadedApk.ReceiverDispatcher rd = map.get(r);
+ return rd == null ? null : rd.getIIntentReceiver();
+ }
+ return null;
+ }
+ }
+
public IIntentReceiver forgetReceiverDispatcher(Context context,
BroadcastReceiver r) {
synchronized (mReceivers) {
diff --git a/core/java/android/app/NotificationChannel.java b/core/java/android/app/NotificationChannel.java
index ebe7b3a4fc51..73d26b834497 100644
--- a/core/java/android/app/NotificationChannel.java
+++ b/core/java/android/app/NotificationChannel.java
@@ -15,6 +15,8 @@
*/
package android.app;
+import static android.service.notification.Flags.FLAG_NOTIFICATION_CONVERSATION_CHANNEL_MANAGEMENT;
+
import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -475,9 +477,10 @@ public final class NotificationChannel implements Parcelable {
dest.writeBoolean(mImportanceLockedDefaultApp);
}
- /**
- * @hide
- */
+ /** @hide */
+ @TestApi
+ @NonNull
+ @FlaggedApi(FLAG_NOTIFICATION_CONVERSATION_CHANNEL_MANAGEMENT)
public NotificationChannel copy() {
NotificationChannel copy = new NotificationChannel(mId, mName, mImportance);
copy.setDescription(mDesc);
@@ -548,10 +551,10 @@ public final class NotificationChannel implements Parcelable {
mDeletedTime = time;
}
- /**
- * @hide
- */
+ /** @hide */
@TestApi
+ @SystemApi
+ @FlaggedApi(FLAG_NOTIFICATION_CONVERSATION_CHANNEL_MANAGEMENT)
public void setImportantConversation(boolean importantConvo) {
mImportantConvo = importantConvo;
}
diff --git a/core/java/android/app/admin/flags/flags.aconfig b/core/java/android/app/admin/flags/flags.aconfig
index fd583773d4e9..3aaca25eca15 100644
--- a/core/java/android/app/admin/flags/flags.aconfig
+++ b/core/java/android/app/admin/flags/flags.aconfig
@@ -341,3 +341,11 @@ flag {
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ name: "set_mte_policy_coexistence"
+ is_exported: true
+ namespace: "enterprise"
+ description: "Enables coexistence support for Setting MTE policy."
+ bug: "376213673"
+}
diff --git a/core/java/android/app/appfunctions/AppFunctionManager.java b/core/java/android/app/appfunctions/AppFunctionManager.java
index 8944bb9c9c4c..5ddb590add4c 100644
--- a/core/java/android/app/appfunctions/AppFunctionManager.java
+++ b/core/java/android/app/appfunctions/AppFunctionManager.java
@@ -50,29 +50,39 @@ import java.util.function.Consumer;
* <p>**Building App Functions:**
*
* <p>Most developers should build app functions through the AppFunctions SDK. This SDK library
- * offers a more convenient and type-safe way to represent the inputs and outputs of an app
- * function, using custom data classes called "AppFunction Schemas".
+ * offers a more convenient and type-safe way to build app functions. The SDK provides predefined
+ * function schemas for common use cases and associated data classes for function parameters and
+ * return values. Apps only have to implement the provided interfaces. Internally, the SDK converts
+ * these data classes into {@link ExecuteAppFunctionRequest#getParameters()} and {@link
+ * ExecuteAppFunctionResponse#getResultDocument()}.
*
- * <p>The suggested way to build an app function is to use the AppFunctions SDK. The SDK provides
- * custom data classes (AppFunctions Schemas) and handles the conversion to the underlying {@link
- * android.app.appsearch.GenericDocument}/{@link android.os.Bundle} format used in {@link
- * ExecuteAppFunctionRequest} and {@link ExecuteAppFunctionResponse}.
- *
- * <p>**Discovering (Listing) App Functions:**
+ * <p>**Discovering App Functions:**
*
* <p>When there is a package change or the device starts up, the metadata of available functions is
- * indexed on-device by {@link AppSearchManager}. AppSearch stores the indexed information as a
- * {@code AppFunctionStaticMetadata} document. This allows other apps and the app itself to discover
- * these functions using the AppSearch search APIs. Visibility to this metadata document is based on
- * the packages that have visibility to the app providing the app functions.
+ * indexed on-device by {@link AppSearchManager}. AppSearch stores the indexed information as an
+ * {@code AppFunctionStaticMetadata} document. This document contains the {@code functionIdentifier}
+ * and the schema information that the app function implements. This allows other apps and the app
+ * itself to discover these functions using the AppSearch search APIs. Visibility to this metadata
+ * document is based on the packages that have visibility to the app providing the app functions.
+ * AppFunction SDK provides a convenient way to achieve this and is the preferred method.
*
* <p>**Executing App Functions:**
*
- * <p>Requests to execute a function are built using the {@link ExecuteAppFunctionRequest} class.
- * Callers need the {@code android.permission.EXECUTE_APP_FUNCTIONS} or {@code
+ * <p>To execute an app function, the caller app can retrieve the {@code functionIdentifier} from
+ * the {@code AppFunctionStaticMetadata} document and use it to build an {@link
+ * ExecuteAppFunctionRequest}. Then, invoke {@link #executeAppFunction} with the request to execute
+ * the app function. Callers need the {@code android.permission.EXECUTE_APP_FUNCTIONS} or {@code
* android.permission.EXECUTE_APP_FUNCTIONS_TRUSTED} permission to execute app functions from other
- * apps. An app has automatic visibility to its own functions and doesn't need these permissions to
- * call its own functions via {@code AppFunctionManager}.
+ * apps. An app can always execute its own app functions and doesn't need these permissions.
+ * AppFunction SDK provides a convenient way to achieve this and is the preferred method.
+ *
+ * <p>**Example:**
+ *
+ * <p>An assistant app is trying to fulfill the user request "Save XYZ into my note". The assistant
+ * app should first list all available app functions as {@code AppFunctionStaticMetadata} documents
+ * from AppSearch. Then, it should identify an app function that implements the {@code CreateNote}
+ * schema. Finally, the assistant app can invoke {@link #executeAppFunction} with the {@code
+ * functionIdentifier} of the chosen function.
*/
@FlaggedApi(FLAG_ENABLE_APP_FUNCTION_MANAGER)
@SystemService(Context.APP_FUNCTION_SERVICE)
@@ -202,12 +212,13 @@ public final class AppFunctionManager {
/**
* Returns a boolean through a callback, indicating whether the app function is enabled.
*
- * <p>* This method can only check app functions owned by the caller, or those where the caller
+ * <p>This method can only check app functions owned by the caller, or those where the caller
* has visibility to the owner package and holds either the {@link
* Manifest.permission#EXECUTE_APP_FUNCTIONS} or {@link
* Manifest.permission#EXECUTE_APP_FUNCTIONS_TRUSTED} permission.
*
- * <p>If operation fails, the callback's {@link OutcomeReceiver#onError} is called with errors:
+ * <p>If the operation fails, the callback's {@link OutcomeReceiver#onError} is called with
+ * errors:
*
* <ul>
* <li>{@link IllegalArgumentException}, if the function is not found or the caller does not
@@ -221,23 +232,47 @@ public final class AppFunctionManager {
* @param executor the executor to run the request
* @param callback the callback to receive the function enabled check result
*/
+ @RequiresPermission(
+ anyOf = {
+ Manifest.permission.EXECUTE_APP_FUNCTIONS_TRUSTED,
+ Manifest.permission.EXECUTE_APP_FUNCTIONS
+ },
+ conditional = true)
public void isAppFunctionEnabled(
@NonNull String functionIdentifier,
@NonNull String targetPackage,
@NonNull Executor executor,
@NonNull OutcomeReceiver<Boolean, Exception> callback) {
- Objects.requireNonNull(functionIdentifier);
- Objects.requireNonNull(targetPackage);
- Objects.requireNonNull(executor);
- Objects.requireNonNull(callback);
- AppSearchManager appSearchManager = mContext.getSystemService(AppSearchManager.class);
- if (appSearchManager == null) {
- callback.onError(new IllegalStateException("Failed to get AppSearchManager."));
- return;
- }
+ isAppFunctionEnabledInternal(functionIdentifier, targetPackage, executor, callback);
+ }
- AppFunctionManagerHelper.isAppFunctionEnabled(
- functionIdentifier, targetPackage, appSearchManager, executor, callback);
+ /**
+ * Returns a boolean through a callback, indicating whether the app function is enabled.
+ *
+ * <p>This method can only check app functions owned by the caller, unlike {@link
+ * #isAppFunctionEnabled(String, String, Executor, OutcomeReceiver)}, which allows specifying a
+ * different target package.
+ *
+ * <p>If the operation fails, the callback's {@link OutcomeReceiver#onError} is called with
+ * errors:
+ *
+ * <ul>
+ * <li>{@link IllegalArgumentException}, if the function is not found or the caller does not
+ * have access to it.
+ * </ul>
+ *
+ * @param functionIdentifier the identifier of the app function to check (unique within the
+ * target package) and in most cases, these are automatically generated by the AppFunctions
+ * SDK
+ * @param executor the executor to run the request
+ * @param callback the callback to receive the function enabled check result
+ */
+ public void isAppFunctionEnabled(
+ @NonNull String functionIdentifier,
+ @NonNull Executor executor,
+ @NonNull OutcomeReceiver<Boolean, Exception> callback) {
+ isAppFunctionEnabledInternal(
+ functionIdentifier, mContext.getPackageName(), executor, callback);
}
/**
@@ -280,6 +315,25 @@ public final class AppFunctionManager {
}
}
+ private void isAppFunctionEnabledInternal(
+ @NonNull String functionIdentifier,
+ @NonNull String targetPackage,
+ @NonNull Executor executor,
+ @NonNull OutcomeReceiver<Boolean, Exception> callback) {
+ Objects.requireNonNull(functionIdentifier);
+ Objects.requireNonNull(targetPackage);
+ Objects.requireNonNull(executor);
+ Objects.requireNonNull(callback);
+ AppSearchManager appSearchManager = mContext.getSystemService(AppSearchManager.class);
+ if (appSearchManager == null) {
+ callback.onError(new IllegalStateException("Failed to get AppSearchManager."));
+ return;
+ }
+
+ AppFunctionManagerHelper.isAppFunctionEnabled(
+ functionIdentifier, targetPackage, appSearchManager, executor, callback);
+ }
+
private static class CallbackWrapper extends IAppFunctionEnabledCallback.Stub {
private final OutcomeReceiver<Void, Exception> mCallback;
diff --git a/core/java/android/app/appfunctions/ExecuteAppFunctionRequest.java b/core/java/android/app/appfunctions/ExecuteAppFunctionRequest.java
index 4c5e8c130a73..41bb62270e9f 100644
--- a/core/java/android/app/appfunctions/ExecuteAppFunctionRequest.java
+++ b/core/java/android/app/appfunctions/ExecuteAppFunctionRequest.java
@@ -114,18 +114,14 @@ public final class ExecuteAppFunctionRequest implements Parcelable {
* <p>The bundle may have missing parameters. Developers are advised to implement defensive
* handling measures.
*
- * <p>Similar to {@link #getFunctionIdentifier()} the parameters required by a function can be
- * obtained by querying AppSearch for the corresponding {@code AppFunctionStaticMetadata}. This
- * metadata will contain enough information for the caller to resolve the required parameters
- * either using information from the metadata itself or using the AppFunction SDK for function
- * callers.
+ * @see AppFunctionManager on how to determine the expected parameters.
*/
@NonNull
public GenericDocument getParameters() {
return mParameters.getValue();
}
- /** Returns the additional data relevant to this function execution. */
+ /** Returns the additional metadata for this function execution request. */
@NonNull
public Bundle getExtras() {
return mExtras;
@@ -153,19 +149,31 @@ public final class ExecuteAppFunctionRequest implements Parcelable {
@NonNull
private GenericDocument mParameters = new GenericDocument.Builder<>("", "", "").build();
+ /**
+ * Creates a new instance of this builder class.
+ *
+ * @param targetPackageName The package name of the target app providing the app function to
+ * invoke.
+ * @param functionIdentifier The identifier used by the {@link AppFunctionService} from the
+ * target app to uniquely identify the function to be invoked.
+ */
public Builder(@NonNull String targetPackageName, @NonNull String functionIdentifier) {
mTargetPackageName = Objects.requireNonNull(targetPackageName);
mFunctionIdentifier = Objects.requireNonNull(functionIdentifier);
}
- /** Sets the additional data relevant to this function execution. */
+ /** Sets the additional metadata for this function execution request. */
@NonNull
public Builder setExtras(@NonNull Bundle extras) {
mExtras = Objects.requireNonNull(extras);
return this;
}
- /** Sets the function parameters. */
+ /**
+ * Sets the function parameters.
+ *
+ * @see #ExecuteAppFunctionRequest#getParameters()
+ */
@NonNull
public Builder setParameters(@NonNull GenericDocument parameters) {
Objects.requireNonNull(parameters);
diff --git a/core/java/android/app/appfunctions/ExecuteAppFunctionResponse.java b/core/java/android/app/appfunctions/ExecuteAppFunctionResponse.java
index c907ef114286..cdf02e6f5a09 100644
--- a/core/java/android/app/appfunctions/ExecuteAppFunctionResponse.java
+++ b/core/java/android/app/appfunctions/ExecuteAppFunctionResponse.java
@@ -73,38 +73,102 @@ public final class ExecuteAppFunctionResponse implements Parcelable {
*/
public static final String PROPERTY_RETURN_VALUE = "returnValue";
- /** The call was successful. */
+ /**
+ * The call was successful.
+ *
+ * <p>This result code does not belong in an error category.
+ */
public static final int RESULT_OK = 0;
- /** The caller does not have the permission to execute an app function. */
- public static final int RESULT_DENIED = 1;
+ /**
+ * The caller does not have the permission to execute an app function.
+ *
+ * <p>This error is in the {@link #ERROR_CATEGORY_REQUEST_ERROR} category.
+ */
+ public static final int RESULT_DENIED = 1000;
+
+ /**
+ * The caller supplied invalid arguments to the execution request.
+ *
+ * <p>This error may be considered similar to {@link IllegalArgumentException}.
+ *
+ * <p>This error is in the {@link #ERROR_CATEGORY_REQUEST_ERROR} category.
+ */
+ public static final int RESULT_INVALID_ARGUMENT = 1001;
+
+ /**
+ * The caller tried to execute a disabled app function.
+ *
+ * <p>This error is in the {@link #ERROR_CATEGORY_REQUEST_ERROR} category.
+ */
+ public static final int RESULT_DISABLED = 1002;
+
+ /**
+ * The caller tried to execute a function that does not exist.
+ *
+ * <p>This error is in the {@link #ERROR_CATEGORY_REQUEST_ERROR} category.
+ */
+ public static final int RESULT_FUNCTION_NOT_FOUND = 1003;
+
+ /**
+ * An internal unexpected error coming from the system.
+ *
+ * <p>This error is in the {@link #ERROR_CATEGORY_SYSTEM} category.
+ */
+ public static final int RESULT_SYSTEM_ERROR = 2000;
+
+ /**
+ * The operation was cancelled. Use this error code to report that a cancellation is done after
+ * receiving a cancellation signal.
+ *
+ * <p>This error is in the {@link #ERROR_CATEGORY_SYSTEM} category.
+ */
+ public static final int RESULT_CANCELLED = 2001;
/**
* 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
* unexpected error is thrown from the bound application.
+ *
+ * <p>This error is in the {@link #ERROR_CATEGORY_APP} category.
*/
- public static final int RESULT_APP_UNKNOWN_ERROR = 2;
+ public static final int RESULT_APP_UNKNOWN_ERROR = 3000;
- /** An internal unexpected error coming from the system. */
- public static final int RESULT_INTERNAL_ERROR = 3;
+ /**
+ * The error category is unknown.
+ *
+ * <p>This is the default value for {@link #getErrorCategory}.
+ */
+ public static final int ERROR_CATEGORY_UNKNOWN = 0;
/**
- * The caller supplied invalid arguments to the call.
+ * The error is caused by the app requesting a function execution.
*
- * <p>This error may be considered similar to {@link IllegalArgumentException}.
+ * <p>For example, the caller provided invalid parameters in the execution request e.g. an
+ * invalid function ID.
+ *
+ * <p>Errors in the category fall in the range 1000-1999 inclusive.
*/
- public static final int RESULT_INVALID_ARGUMENT = 4;
+ public static final int ERROR_CATEGORY_REQUEST_ERROR = 1;
- /** The caller tried to execute a disabled app function. */
- public static final int RESULT_DISABLED = 5;
+ /**
+ * The error is caused by an issue in the system.
+ *
+ * <p>For example, the AppFunctionService implementation is not found by the system.
+ *
+ * <p>Errors in the category fall in the range 2000-2999 inclusive.
+ */
+ public static final int ERROR_CATEGORY_SYSTEM = 2;
/**
- * The operation was cancelled. Use this error code to report that a cancellation is done after
- * receiving a cancellation signal.
+ * The error is caused by the app providing the function.
+ *
+ * <p>For example, the app crashed when the system is executing the request.
+ *
+ * <p>Errors in the category fall in the range 3000-3999 inclusive.
*/
- public static final int RESULT_CANCELLED = 6;
+ public static final int ERROR_CATEGORY_APP = 3;
/** The result code of the app function execution. */
@ResultCode private final int mResultCode;
@@ -156,7 +220,7 @@ public final class ExecuteAppFunctionResponse implements Parcelable {
* Returns a successful response.
*
* @param resultDocument The return value of the executed function.
- * @param extras The additional metadata data relevant to this function execution response.
+ * @param extras The additional metadata for this function execution response.
*/
@NonNull
@FlaggedApi(FLAG_ENABLE_APP_FUNCTION_MANAGER)
@@ -174,7 +238,7 @@ public final class ExecuteAppFunctionResponse implements Parcelable {
* Returns a failure response.
*
* @param resultCode The result code of the app function execution.
- * @param extras The additional metadata data relevant to this function execution response.
+ * @param extras The additional metadata for this function execution response.
* @param errorMessage The error message associated with the result, if any.
*/
@NonNull
@@ -198,6 +262,36 @@ public final class ExecuteAppFunctionResponse implements Parcelable {
}
/**
+ * Returns the error category of the {@link ExecuteAppFunctionResponse}.
+ *
+ * <p>This method categorizes errors based on their underlying cause, allowing developers to
+ * implement targeted error handling and provide more informative error messages to users. It
+ * maps ranges of result codes to specific error categories.
+ *
+ * <p>When constructing a {@link #newFailure} response, use the appropriate result code value to
+ * ensure correct categorization of the failed response.
+ *
+ * <p>This method returns {@code ERROR_CATEGORY_UNKNOWN} if the result code does not belong to
+ * any error category, for example, in the case of a successful result with {@link #RESULT_OK}.
+ *
+ * <p>See {@link ErrorCategory} for a complete list of error categories and their corresponding
+ * result code ranges.
+ */
+ @ErrorCategory
+ public int getErrorCategory() {
+ if (mResultCode >= 1000 && mResultCode < 2000) {
+ return ERROR_CATEGORY_REQUEST_ERROR;
+ }
+ if (mResultCode >= 2000 && mResultCode < 3000) {
+ return ERROR_CATEGORY_SYSTEM;
+ }
+ if (mResultCode >= 3000 && mResultCode < 4000) {
+ return ERROR_CATEGORY_APP;
+ }
+ return ERROR_CATEGORY_UNKNOWN;
+ }
+
+ /**
* Returns a generic document containing the return value of the executed function.
*
* <p>The {@link #PROPERTY_RETURN_VALUE} key can be used to obtain the return value.
@@ -216,13 +310,15 @@ public final class ExecuteAppFunctionResponse implements Parcelable {
* // Do something with the returnValue
* }
* </pre>
+ *
+ * @see AppFunctionManager on how to determine the expected function return.
*/
@NonNull
public GenericDocument getResultDocument() {
return mResultDocumentWrapper.getValue();
}
- /** Returns the extras of the app function execution. */
+ /** Returns the additional metadata for this function execution response. */
@NonNull
public Bundle getExtras() {
return mExtras;
@@ -278,11 +374,28 @@ public final class ExecuteAppFunctionResponse implements Parcelable {
RESULT_OK,
RESULT_DENIED,
RESULT_APP_UNKNOWN_ERROR,
- RESULT_INTERNAL_ERROR,
+ RESULT_FUNCTION_NOT_FOUND,
+ RESULT_SYSTEM_ERROR,
RESULT_INVALID_ARGUMENT,
RESULT_DISABLED,
RESULT_CANCELLED
})
@Retention(RetentionPolicy.SOURCE)
public @interface ResultCode {}
+
+ /**
+ * Error categories.
+ *
+ * @hide
+ */
+ @IntDef(
+ prefix = {"ERROR_CATEGORY_"},
+ value = {
+ ERROR_CATEGORY_UNKNOWN,
+ ERROR_CATEGORY_REQUEST_ERROR,
+ ERROR_CATEGORY_APP,
+ ERROR_CATEGORY_SYSTEM
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ErrorCategory {}
}
diff --git a/core/java/android/app/backup/NotificationLoggingConstants.java b/core/java/android/app/backup/NotificationLoggingConstants.java
index add456286df3..3601e862c02f 100644
--- a/core/java/android/app/backup/NotificationLoggingConstants.java
+++ b/core/java/android/app/backup/NotificationLoggingConstants.java
@@ -23,10 +23,56 @@ public class NotificationLoggingConstants {
// Key under which the payload blob is stored
public static final String KEY_NOTIFICATIONS = "notifications";
+ /**
+ * Events for android.service.notification.ZenModeConfig - the configuration for all modes
+ * settings for a single user
+ */
@BackupRestoreEventLogger.BackupRestoreDataType
public static final String DATA_TYPE_ZEN_CONFIG = KEY_NOTIFICATIONS + ":zen_config";
+ /**
+ * Events for android.service.notification.ZenModeConfig.ZenRule - a single mode within a
+ * ZenModeConfig
+ */
@BackupRestoreEventLogger.BackupRestoreDataType
public static final String DATA_TYPE_ZEN_RULES = KEY_NOTIFICATIONS + ":zen_rules";
+ /**
+ * Events for globally stored notifications data that aren't stored in settings, like whether
+ * to hide silent notification in the status bar
+ */
+ @BackupRestoreEventLogger.BackupRestoreDataType
+ public static final String DATA_TYPE_NOTIF_GLOBAL = KEY_NOTIFICATIONS + ":global";
+ /**
+ * Events for package specific notification settings, including app and
+ * android.app.NotificationChannel level settings.
+ */
+ @BackupRestoreEventLogger.BackupRestoreDataType
+ public static final String DATA_TYPE_NOTIF_PACKAGES = KEY_NOTIFICATIONS + ":packages";
+ /**
+ * Events for approved ManagedServices (NotificationListenerServices,
+ * NotificationAssistantService, ConditionProviderService).
+ */
+ @BackupRestoreEventLogger.BackupRestoreDataType
+ public static final String DATA_TYPE_MANAGED_SERVICE_PRIMARY_APPROVED = KEY_NOTIFICATIONS +
+ ":managed_service_primary_approved";
+ /**
+ * Events for what types of notifications NotificationListenerServices cannot see
+ */
+ @BackupRestoreEventLogger.BackupRestoreDataType
+ public static final String DATA_TYPE_NLS_RESTRICTED = KEY_NOTIFICATIONS +
+ ":nls_restricted";
+ /**
+ * Events for ManagedServices that are approved because they have a different primary
+ * ManagedService (ConditionProviderService).
+ */
+ @BackupRestoreEventLogger.BackupRestoreDataType
+ public static final String DATA_TYPE_MANAGED_SERVICE_SECONDARY_APPROVED = KEY_NOTIFICATIONS +
+ ":managed_service_secondary_approved";
+ /**
+ * Events for individual snoozed notifications.
+ */
+ @BackupRestoreEventLogger.BackupRestoreDataType
+ public static final String DATA_TYPE_SNOOZED = KEY_NOTIFICATIONS + ":snoozed";
+
@BackupRestoreEventLogger.BackupRestoreError
public static final String ERROR_XML_PARSING = KEY_NOTIFICATIONS + ":invalid_xml_parsing";
diff --git a/core/java/android/app/contextualsearch/flags.aconfig b/core/java/android/app/contextualsearch/flags.aconfig
index 3b0c86795738..5e09517d1edc 100644
--- a/core/java/android/app/contextualsearch/flags.aconfig
+++ b/core/java/android/app/contextualsearch/flags.aconfig
@@ -12,4 +12,18 @@ flag {
namespace: "machine_learning"
description: "Flag to refresh the token to the callback"
bug: "309689654"
+}
+
+flag {
+ name: "multi_window_screen_context"
+ namespace: "machine_learning"
+ description: "Report screen context and positions for all windows."
+ bug: "371065456"
+}
+
+flag {
+ name: "contextual_search_window_layer"
+ namespace: "machine_learning"
+ 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/appwidget/AppWidgetHost.java b/core/java/android/appwidget/AppWidgetHost.java
index 72f992ab3917..79eeaa914771 100644
--- a/core/java/android/appwidget/AppWidgetHost.java
+++ b/core/java/android/appwidget/AppWidgetHost.java
@@ -306,6 +306,38 @@ public class AppWidgetHost {
}
/**
+ * Returns an {@link IntentSender} for starting the configuration activity for the widget.
+ *
+ * @return The {@link IntentSender} or null if service is currently unavailable
+ *
+ * @throws android.content.ActivityNotFoundException If configuration activity is not found.
+ *
+ * @see #startAppWidgetConfigureActivityForResult
+ *
+ * @hide
+ */
+ @Nullable
+ public final IntentSender getIntentSenderForConfigureActivity(int appWidgetId,
+ int intentFlags) {
+ if (sService == null) {
+ return null;
+ }
+
+ IntentSender intentSender;
+ try {
+ intentSender = sService.createAppWidgetConfigIntentSender(mContextOpPackageName,
+ appWidgetId, intentFlags);
+ } catch (RemoteException e) {
+ throw new RuntimeException("system server dead?", e);
+ }
+
+ if (intentSender == null) {
+ throw new ActivityNotFoundException();
+ }
+ return intentSender;
+ }
+
+ /**
* Starts an app widget provider configure activity for result on behalf of the caller.
* Use this method if the provider is in another profile as you are not allowed to start
* an activity in another profile. You can optionally provide a request code that is
@@ -330,18 +362,11 @@ public class AppWidgetHost {
return;
}
try {
- IntentSender intentSender = sService.createAppWidgetConfigIntentSender(
- mContextOpPackageName, appWidgetId, intentFlags);
- if (intentSender != null) {
- activity.startIntentSenderForResult(intentSender, requestCode, null, 0, 0, 0,
- options);
- } else {
- throw new ActivityNotFoundException();
- }
+ IntentSender intentSender = getIntentSenderForConfigureActivity(appWidgetId,
+ intentFlags);
+ activity.startIntentSenderForResult(intentSender, requestCode, null, 0, 0, 0, options);
} catch (IntentSender.SendIntentException e) {
throw new ActivityNotFoundException();
- } catch (RemoteException e) {
- throw new RuntimeException("system server dead?", e);
}
}
diff --git a/core/java/android/companion/virtual/flags/flags.aconfig b/core/java/android/companion/virtual/flags/flags.aconfig
index 9eb6d5624f75..9af20164a66d 100644
--- a/core/java/android/companion/virtual/flags/flags.aconfig
+++ b/core/java/android/companion/virtual/flags/flags.aconfig
@@ -145,3 +145,11 @@ flag {
bug: "370657575"
is_exported: true
}
+
+flag {
+ namespace: "virtual_devices"
+ name: "default_device_camera_access_policy"
+ description: "API for default device camera access policy"
+ bug: "371173368"
+ is_exported: true
+}
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index b22caf71a8a7..186f7b3e111c 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -3658,6 +3658,27 @@ public abstract class Context {
public abstract void unregisterReceiver(BroadcastReceiver receiver);
/**
+ * Returns the list of {@link IntentFilter} objects that have been registered for the given
+ * {@link BroadcastReceiver}.
+ *
+ * @param receiver The {@link BroadcastReceiver} whose registered intent filters
+ * should be retrieved.
+ *
+ * @return A list of registered intent filters, or an empty list if the given receiver is not
+ * registered.
+ *
+ * @throws NullPointerException if the {@code receiver} is {@code null}.
+ *
+ * @hide
+ */
+ @SuppressLint("UnflaggedApi") // TestApi
+ @TestApi
+ @NonNull
+ public List<IntentFilter> getRegisteredIntentFilters(@NonNull BroadcastReceiver receiver) {
+ throw new RuntimeException("Not implemented. Must override in a subclass.");
+ }
+
+ /**
* Request that a given application service be started. The Intent
* should either contain the complete class name of a specific service
* implementation to start, or a specific package name to target. If the
@@ -4873,6 +4894,8 @@ public abstract class Context {
* @see android.net.vcn.VcnManager
* @hide
*/
+ @FlaggedApi(android.os.Flags.FLAG_MAINLINE_VCN_PLATFORM_API)
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
public static final String VCN_MANAGEMENT_SERVICE = "vcn_management";
/**
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index 79fa6ea4d157..23d17cb5ce50 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -843,6 +843,13 @@ public class ContextWrapper extends Context {
mBase.unregisterReceiver(receiver);
}
+ /** @hide */
+ @Override
+ @NonNull
+ public List<IntentFilter> getRegisteredIntentFilters(@NonNull BroadcastReceiver receiver) {
+ return mBase.getRegisteredIntentFilters(receiver);
+ }
+
@Override
public @Nullable ComponentName startService(Intent service) {
return mBase.startService(service);
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 9bec5981261e..6fa5a9b82858 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -41,10 +41,7 @@ import android.app.Activity;
import android.app.ActivityThread;
import android.app.AppGlobals;
import android.app.StatusBarManager;
-import android.app.compat.CompatChanges;
import android.bluetooth.BluetoothDevice;
-import android.compat.annotation.ChangeId;
-import android.compat.annotation.Overridable;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
@@ -673,12 +670,6 @@ import java.util.TimeZone;
@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class Intent implements Parcelable, Cloneable {
private static final String TAG = "Intent";
-
- /** @hide */
- @ChangeId
- @Overridable
- public static final long ENABLE_PREVENT_INTENT_REDIRECT = 29076063L;
-
private static final String ATTR_ACTION = "action";
private static final String TAG_CATEGORIES = "categories";
private static final String ATTR_CATEGORY = "category";
@@ -908,7 +899,7 @@ public class Intent implements Parcelable, Cloneable {
boolean isForeign = (intent.mLocalFlags & LOCAL_FLAG_FROM_PARCEL) != 0;
boolean isWithoutTrustedCreatorToken =
(intent.mLocalFlags & Intent.LOCAL_FLAG_TRUSTED_CREATOR_TOKEN_PRESENT) == 0;
- if (isForeign && isWithoutTrustedCreatorToken) {
+ if (isForeign && isWithoutTrustedCreatorToken && preventIntentRedirect()) {
intent.addExtendedFlags(EXTENDED_FLAG_MISSING_CREATOR_OR_INVALID_TOKEN);
}
}
@@ -12255,7 +12246,7 @@ public class Intent implements Parcelable, Cloneable {
* @hide
*/
public void collectExtraIntentKeys() {
- if (!isPreventIntentRedirectEnabled()) return;
+ if (!preventIntentRedirect()) return;
if (mExtras != null && !mExtras.isEmpty()) {
for (String key : mExtras.keySet()) {
@@ -12272,14 +12263,6 @@ public class Intent implements Parcelable, Cloneable {
}
}
- /**
- * @hide
- */
- public static boolean isPreventIntentRedirectEnabled() {
- return preventIntentRedirect() && CompatChanges.isChangeEnabled(
- ENABLE_PREVENT_INTENT_REDIRECT);
- }
-
/** @hide */
public void checkCreatorToken() {
if (mExtras == null) return;
@@ -12368,7 +12351,7 @@ public class Intent implements Parcelable, Cloneable {
out.writeInt(0);
}
- if (isPreventIntentRedirectEnabled()) {
+ if (preventIntentRedirect()) {
if (mCreatorTokenInfo == null) {
out.writeInt(0);
} else {
@@ -12435,7 +12418,7 @@ public class Intent implements Parcelable, Cloneable {
mOriginalIntent = new Intent(in);
}
- if (isPreventIntentRedirectEnabled()) {
+ if (preventIntentRedirect()) {
if (in.readInt() != 0) {
mCreatorTokenInfo = new CreatorTokenInfo();
mCreatorTokenInfo.mCreatorToken = in.readStrongBinder();
diff --git a/core/java/android/content/om/OverlayManager.java b/core/java/android/content/om/OverlayManager.java
index 6db7dfe4f705..ed965b3d1777 100644
--- a/core/java/android/content/om/OverlayManager.java
+++ b/core/java/android/content/om/OverlayManager.java
@@ -78,8 +78,7 @@ public class OverlayManager {
/**
* Applications can use OverlayManager to create overlays to overlay on itself resources. The
- * overlay target is itself, or the Android package, and the work range is only in caller
- * application.
+ * overlay target is itself and the work range is only in caller application.
*
* <p>In {@link android.content.Context#getSystemService(String)}, it crashes because of {@link
* java.lang.NullPointerException} if the parameter is OverlayManager. if the self-targeting is
@@ -402,7 +401,7 @@ public class OverlayManager {
}
/**
- * Get the related information of self-targeting overlays for {@code targetPackageName}.
+ * Get the related information of overlays for {@code targetPackageName}.
*
* @param targetPackageName the target package name
* @return a list of overlay information
diff --git a/core/java/android/content/om/OverlayManagerTransaction.java b/core/java/android/content/om/OverlayManagerTransaction.java
index 87b2e9350aa1..becd0ea285af 100644
--- a/core/java/android/content/om/OverlayManagerTransaction.java
+++ b/core/java/android/content/om/OverlayManagerTransaction.java
@@ -209,7 +209,6 @@ public final class OverlayManagerTransaction implements Parcelable {
*/
public static final class Builder {
private final List<Request> mRequests = new ArrayList<>();
- private boolean mSelfTargeting = false;
/**
* Request that an overlay package be enabled and change its loading
@@ -247,18 +246,6 @@ public final class OverlayManagerTransaction implements Parcelable {
}
/**
- * Request that an overlay package be self-targeting. Self-targeting overlays enable
- * applications to overlay on itself resources. The overlay target is itself, or the Android
- * package, and the work range is only in caller application.
- * @param selfTargeting whether the overlay is self-targeting, the default is false.
- * @hide
- */
- public Builder setSelfTargeting(boolean selfTargeting) {
- mSelfTargeting = selfTargeting;
- return this;
- }
-
- /**
* Registers the fabricated overlay with the overlay manager so it can be enabled and
* disabled for any user.
*
@@ -299,7 +286,7 @@ public final class OverlayManagerTransaction implements Parcelable {
*/
@NonNull
public OverlayManagerTransaction build() {
- return new OverlayManagerTransaction(mRequests, mSelfTargeting);
+ return new OverlayManagerTransaction(mRequests, false /* selfTargeting */);
}
}
diff --git a/core/java/android/content/pm/multiuser.aconfig b/core/java/android/content/pm/multiuser.aconfig
index 35f9cff1aedb..528bde80cd3d 100644
--- a/core/java/android/content/pm/multiuser.aconfig
+++ b/core/java/android/content/pm/multiuser.aconfig
@@ -536,7 +536,7 @@ flag {
flag {
name: "ignore_restrictions_when_deleting_private_profile"
- namespace: "multiuser"
+ namespace: "profile_experiences"
description: "Ignore any user restrictions when deleting private profiles."
bug: "350953833"
metadata {
diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java
index 347bebdf46e7..6fd4d0141977 100644
--- a/core/java/android/content/res/AssetManager.java
+++ b/core/java/android/content/res/AssetManager.java
@@ -75,10 +75,7 @@ public final class AssetManager implements AutoCloseable {
private static final String TAG = "AssetManager";
private static final boolean DEBUG_REFS = false;
- /**
- * @hide
- */
- public static final String FRAMEWORK_APK_PATH = getFrameworkApkPath();
+ private static final String FRAMEWORK_APK_PATH = getFrameworkApkPath();
private static final String FRAMEWORK_APK_PATH_DEVICE = "/system/framework/framework-res.apk";
private static final String FRAMEWORK_APK_PATH_RAVENWOOD = "ravenwood-data/framework-res.apk";
diff --git a/core/java/android/content/res/flags.aconfig b/core/java/android/content/res/flags.aconfig
index 0af2f2576137..e98fc0c9d02c 100644
--- a/core/java/android/content/res/flags.aconfig
+++ b/core/java/android/content/res/flags.aconfig
@@ -74,3 +74,12 @@ flag {
description: "Feature flag for passing a dimension to create an frro"
bug: "369672322"
}
+
+flag {
+ name: "rro_control_for_android_no_overlayable"
+ is_exported: true
+ namespace: "resource_manager"
+ description: "Allow enabling and disabling RROs targeting android package with no overlayable"
+ bug: "364035303"
+}
+
diff --git a/core/java/android/content/res/loader/ResourcesProvider.java b/core/java/android/content/res/loader/ResourcesProvider.java
index 830b7e0fa2d0..b097bc0dcd8c 100644
--- a/core/java/android/content/res/loader/ResourcesProvider.java
+++ b/core/java/android/content/res/loader/ResourcesProvider.java
@@ -90,6 +90,8 @@ public class ResourcesProvider implements AutoCloseable, Closeable {
throws IOException {
Objects.requireNonNull(overlayInfo);
Preconditions.checkArgument(overlayInfo.isFabricated(), "Not accepted overlay");
+ Preconditions.checkStringNotEmpty(
+ overlayInfo.getTargetOverlayableName(), "Without overlayable name");
final String overlayName =
OverlayManagerImpl.checkOverlayNameValid(overlayInfo.getOverlayName());
final String path =
diff --git a/core/java/android/database/sqlite/SQLiteConnection.java b/core/java/android/database/sqlite/SQLiteConnection.java
index d77e6280d19c..75c7e267d477 100644
--- a/core/java/android/database/sqlite/SQLiteConnection.java
+++ b/core/java/android/database/sqlite/SQLiteConnection.java
@@ -241,7 +241,8 @@ public final class SQLiteConnection implements CancellationSignal.OnCancelListen
NoPreloadHolder.DEBUG_SQL_STATEMENTS, NoPreloadHolder.DEBUG_SQL_TIME,
mConfiguration.lookasideSlotSize, mConfiguration.lookasideSlotCount);
} catch (SQLiteCantOpenDatabaseException e) {
- final StringBuilder message = new StringBuilder(e.getMessage())
+ final StringBuilder message = new StringBuilder("Cannot open database ")
+ .append("[").append(e.getMessage()).append("]")
.append(" '").append(file).append("'")
.append(" with flags 0x")
.append(Integer.toHexString(mConfiguration.openFlags));
diff --git a/core/java/android/hardware/biometrics/BiometricPrompt.java b/core/java/android/hardware/biometrics/BiometricPrompt.java
index e3fdd267623e..3ff21d85507b 100644
--- a/core/java/android/hardware/biometrics/BiometricPrompt.java
+++ b/core/java/android/hardware/biometrics/BiometricPrompt.java
@@ -22,7 +22,6 @@ import static android.Manifest.permission.USE_BIOMETRIC;
import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL;
import static android.hardware.biometrics.BiometricManager.Authenticators;
import static android.hardware.biometrics.Flags.FLAG_ADD_KEY_AGREEMENT_CRYPTO_OBJECT;
-import static android.hardware.biometrics.Flags.FLAG_CUSTOM_BIOMETRIC_PROMPT;
import static android.hardware.biometrics.Flags.FLAG_GET_OP_ID_CRYPTO_OBJECT;
import static android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE;
@@ -200,7 +199,6 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan
* @param logoRes A drawable resource of the logo that will be shown on the prompt.
* @return This builder.
*/
- @FlaggedApi(FLAG_CUSTOM_BIOMETRIC_PROMPT)
@RequiresPermission(SET_BIOMETRIC_DIALOG_ADVANCED)
@NonNull
public BiometricPrompt.Builder setLogoRes(@DrawableRes int logoRes) {
@@ -226,7 +224,6 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan
* @param logoBitmap A bitmap drawable of the logo that will be shown on the prompt.
* @return This builder.
*/
- @FlaggedApi(FLAG_CUSTOM_BIOMETRIC_PROMPT)
@RequiresPermission(SET_BIOMETRIC_DIALOG_ADVANCED)
@NonNull
public BiometricPrompt.Builder setLogoBitmap(@NonNull Bitmap logoBitmap) {
@@ -250,7 +247,6 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan
* @return This builder.
* @throws IllegalArgumentException If logo description is null.
*/
- @FlaggedApi(FLAG_CUSTOM_BIOMETRIC_PROMPT)
@RequiresPermission(SET_BIOMETRIC_DIALOG_ADVANCED)
@NonNull
public BiometricPrompt.Builder setLogoDescription(@NonNull String logoDescription) {
@@ -342,7 +338,6 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan
* @param view The customized view information.
* @return This builder.
*/
- @FlaggedApi(FLAG_CUSTOM_BIOMETRIC_PROMPT)
@NonNull
public BiometricPrompt.Builder setContentView(@NonNull PromptContentView view) {
mPromptInfo.setContentView(view);
@@ -851,7 +846,6 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan
*
* @return The drawable resource of the logo, or 0 if the prompt has no logo resource set.
*/
- @FlaggedApi(FLAG_CUSTOM_BIOMETRIC_PROMPT)
@RequiresPermission(SET_BIOMETRIC_DIALOG_ADVANCED)
@DrawableRes
public int getLogoRes() {
@@ -864,7 +858,6 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan
*
* @return The logo bitmap of the prompt, or null if the prompt has no logo bitmap set.
*/
- @FlaggedApi(FLAG_CUSTOM_BIOMETRIC_PROMPT)
@RequiresPermission(SET_BIOMETRIC_DIALOG_ADVANCED)
@Nullable
public Bitmap getLogoBitmap() {
@@ -879,7 +872,6 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan
* @return The logo description of the prompt, or null if the prompt has no logo description
* set.
*/
- @FlaggedApi(FLAG_CUSTOM_BIOMETRIC_PROMPT)
@RequiresPermission(SET_BIOMETRIC_DIALOG_ADVANCED)
@Nullable
public String getLogoDescription() {
@@ -939,7 +931,6 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan
*
* @return The content view for the prompt, or null if the prompt has no content view.
*/
- @FlaggedApi(FLAG_CUSTOM_BIOMETRIC_PROMPT)
@Nullable
public PromptContentView getContentView() {
return mPromptInfo.getContentView();
diff --git a/core/java/android/hardware/biometrics/PromptContentItem.java b/core/java/android/hardware/biometrics/PromptContentItem.java
index c47b37aca2ea..0c41782da9fb 100644
--- a/core/java/android/hardware/biometrics/PromptContentItem.java
+++ b/core/java/android/hardware/biometrics/PromptContentItem.java
@@ -16,14 +16,9 @@
package android.hardware.biometrics;
-import static android.hardware.biometrics.Flags.FLAG_CUSTOM_BIOMETRIC_PROMPT;
-
-import android.annotation.FlaggedApi;
-
/**
* An item shown on {@link PromptContentView}.
*/
-@FlaggedApi(FLAG_CUSTOM_BIOMETRIC_PROMPT)
public interface PromptContentItem {
}
diff --git a/core/java/android/hardware/biometrics/PromptContentItemBulletedText.java b/core/java/android/hardware/biometrics/PromptContentItemBulletedText.java
index 25e5cca485d2..a026498b6e81 100644
--- a/core/java/android/hardware/biometrics/PromptContentItemBulletedText.java
+++ b/core/java/android/hardware/biometrics/PromptContentItemBulletedText.java
@@ -16,9 +16,6 @@
package android.hardware.biometrics;
-import static android.hardware.biometrics.Flags.FLAG_CUSTOM_BIOMETRIC_PROMPT;
-
-import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.os.Parcel;
import android.os.Parcelable;
@@ -26,7 +23,6 @@ import android.os.Parcelable;
/**
* A list item with bulleted text shown on {@link PromptVerticalListContentView}.
*/
-@FlaggedApi(FLAG_CUSTOM_BIOMETRIC_PROMPT)
public final class PromptContentItemBulletedText implements PromptContentItemParcelable {
private final String mText;
diff --git a/core/java/android/hardware/biometrics/PromptContentItemParcelable.java b/core/java/android/hardware/biometrics/PromptContentItemParcelable.java
index 668912cfdf24..1860aae9993a 100644
--- a/core/java/android/hardware/biometrics/PromptContentItemParcelable.java
+++ b/core/java/android/hardware/biometrics/PromptContentItemParcelable.java
@@ -16,15 +16,11 @@
package android.hardware.biometrics;
-import static android.hardware.biometrics.Flags.FLAG_CUSTOM_BIOMETRIC_PROMPT;
-
-import android.annotation.FlaggedApi;
import android.os.Parcelable;
/**
* A parcelable {@link PromptContentItem}.
*/
-@FlaggedApi(FLAG_CUSTOM_BIOMETRIC_PROMPT)
sealed interface PromptContentItemParcelable extends PromptContentItem, Parcelable
permits PromptContentItemPlainText, PromptContentItemBulletedText {
}
diff --git a/core/java/android/hardware/biometrics/PromptContentItemPlainText.java b/core/java/android/hardware/biometrics/PromptContentItemPlainText.java
index 7919256f9c6d..a5e612060575 100644
--- a/core/java/android/hardware/biometrics/PromptContentItemPlainText.java
+++ b/core/java/android/hardware/biometrics/PromptContentItemPlainText.java
@@ -16,9 +16,6 @@
package android.hardware.biometrics;
-import static android.hardware.biometrics.Flags.FLAG_CUSTOM_BIOMETRIC_PROMPT;
-
-import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.os.Parcel;
import android.os.Parcelable;
@@ -26,7 +23,6 @@ import android.os.Parcelable;
/**
* A list item with plain text shown on {@link PromptVerticalListContentView}.
*/
-@FlaggedApi(FLAG_CUSTOM_BIOMETRIC_PROMPT)
public final class PromptContentItemPlainText implements PromptContentItemParcelable {
private final String mText;
diff --git a/core/java/android/hardware/biometrics/PromptContentView.java b/core/java/android/hardware/biometrics/PromptContentView.java
index ff9313e8d3f0..0836d72ccf18 100644
--- a/core/java/android/hardware/biometrics/PromptContentView.java
+++ b/core/java/android/hardware/biometrics/PromptContentView.java
@@ -16,13 +16,8 @@
package android.hardware.biometrics;
-import static android.hardware.biometrics.Flags.FLAG_CUSTOM_BIOMETRIC_PROMPT;
-
-import android.annotation.FlaggedApi;
-
/**
* Contains the information of the template of content view for Biometric Prompt.
*/
-@FlaggedApi(FLAG_CUSTOM_BIOMETRIC_PROMPT)
public interface PromptContentView {
}
diff --git a/core/java/android/hardware/biometrics/PromptContentViewParcelable.java b/core/java/android/hardware/biometrics/PromptContentViewParcelable.java
index b5982d4f7251..6e035634df78 100644
--- a/core/java/android/hardware/biometrics/PromptContentViewParcelable.java
+++ b/core/java/android/hardware/biometrics/PromptContentViewParcelable.java
@@ -16,15 +16,11 @@
package android.hardware.biometrics;
-import static android.hardware.biometrics.Flags.FLAG_CUSTOM_BIOMETRIC_PROMPT;
-
-import android.annotation.FlaggedApi;
import android.os.Parcelable;
/**
* A parcelable {@link PromptContentView}.
*/
-@FlaggedApi(FLAG_CUSTOM_BIOMETRIC_PROMPT)
sealed interface PromptContentViewParcelable extends PromptContentView, Parcelable
permits PromptVerticalListContentView, PromptContentViewWithMoreOptionsButton {
}
diff --git a/core/java/android/hardware/biometrics/PromptContentViewWithMoreOptionsButton.java b/core/java/android/hardware/biometrics/PromptContentViewWithMoreOptionsButton.java
index 4b9d5ce7beb0..aa0ce5843a34 100644
--- a/core/java/android/hardware/biometrics/PromptContentViewWithMoreOptionsButton.java
+++ b/core/java/android/hardware/biometrics/PromptContentViewWithMoreOptionsButton.java
@@ -17,10 +17,8 @@
package android.hardware.biometrics;
import static android.Manifest.permission.SET_BIOMETRIC_DIALOG_ADVANCED;
-import static android.hardware.biometrics.Flags.FLAG_CUSTOM_BIOMETRIC_PROMPT;
import android.annotation.CallbackExecutor;
-import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
@@ -61,7 +59,6 @@ import java.util.concurrent.Executor;
* .build();
* </pre>
*/
-@FlaggedApi(FLAG_CUSTOM_BIOMETRIC_PROMPT)
public final class PromptContentViewWithMoreOptionsButton implements PromptContentViewParcelable {
private static final String TAG = "PromptContentViewWithMoreOptionsButton";
@VisibleForTesting
diff --git a/core/java/android/hardware/biometrics/PromptInfo.java b/core/java/android/hardware/biometrics/PromptInfo.java
index e23ffeb6a0d2..3e304e4a5915 100644
--- a/core/java/android/hardware/biometrics/PromptInfo.java
+++ b/core/java/android/hardware/biometrics/PromptInfo.java
@@ -217,7 +217,7 @@ public class PromptInfo implements Parcelable {
* Returns if the PromptContentViewWithMoreOptionsButton is set.
*/
public boolean isContentViewMoreOptionsButtonUsed() {
- return Flags.customBiometricPrompt() && mContentView != null
+ return mContentView != null
&& mContentView instanceof PromptContentViewWithMoreOptionsButton;
}
diff --git a/core/java/android/hardware/biometrics/PromptVerticalListContentView.java b/core/java/android/hardware/biometrics/PromptVerticalListContentView.java
index 86006f84fe60..2a521d1b1c2d 100644
--- a/core/java/android/hardware/biometrics/PromptVerticalListContentView.java
+++ b/core/java/android/hardware/biometrics/PromptVerticalListContentView.java
@@ -16,9 +16,6 @@
package android.hardware.biometrics;
-import static android.hardware.biometrics.Flags.FLAG_CUSTOM_BIOMETRIC_PROMPT;
-
-import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.Parcel;
@@ -48,7 +45,6 @@ import java.util.List;
* .build();
* </pre>
*/
-@FlaggedApi(FLAG_CUSTOM_BIOMETRIC_PROMPT)
public final class PromptVerticalListContentView implements PromptContentViewParcelable {
private static final String TAG = "PromptVerticalListContentView";
@VisibleForTesting
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index 1137e1e89f67..16d82caa6c1a 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -775,6 +775,46 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri
new Key<int[]>("android.colorCorrection.availableAberrationModes", int[].class);
/**
+ * <p>The range of supported color temperature values for
+ * {@link CaptureRequest#COLOR_CORRECTION_COLOR_TEMPERATURE android.colorCorrection.colorTemperature}.</p>
+ * <p>This key lists the valid range of color temperature values for
+ * {@link CaptureRequest#COLOR_CORRECTION_COLOR_TEMPERATURE android.colorCorrection.colorTemperature} supported by this camera device.</p>
+ * <p>This key will be null on devices that do not support CCT mode for
+ * {@link CaptureRequest#COLOR_CORRECTION_MODE android.colorCorrection.mode}.</p>
+ * <p><b>Range of valid values:</b><br></p>
+ * <p>The minimum supported range will be [2856K,6500K]. The maximum supported
+ * range will be [1000K,40000K].</p>
+ * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+ *
+ * @see CaptureRequest#COLOR_CORRECTION_COLOR_TEMPERATURE
+ * @see CaptureRequest#COLOR_CORRECTION_MODE
+ */
+ @PublicKey
+ @NonNull
+ @FlaggedApi(Flags.FLAG_COLOR_TEMPERATURE)
+ public static final Key<android.util.Range<Integer>> COLOR_CORRECTION_COLOR_TEMPERATURE_RANGE =
+ new Key<android.util.Range<Integer>>("android.colorCorrection.colorTemperatureRange", new TypeReference<android.util.Range<Integer>>() {{ }});
+
+ /**
+ * <p>List of color correction modes for {@link CaptureRequest#COLOR_CORRECTION_MODE android.colorCorrection.mode} that are
+ * supported by this camera device.</p>
+ * <p>This key lists the valid modes for {@link CaptureRequest#COLOR_CORRECTION_MODE android.colorCorrection.mode}. If no
+ * color correction modes are available for a device, this key will be null.</p>
+ * <p>Camera devices that have a FULL hardware level will always include at least
+ * FAST, HIGH_QUALITY, and TRANSFORM_MATRIX modes.</p>
+ * <p><b>Range of valid values:</b><br>
+ * Any value listed in {@link CaptureRequest#COLOR_CORRECTION_MODE android.colorCorrection.mode}</p>
+ * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+ *
+ * @see CaptureRequest#COLOR_CORRECTION_MODE
+ */
+ @PublicKey
+ @NonNull
+ @FlaggedApi(Flags.FLAG_COLOR_TEMPERATURE)
+ public static final Key<int[]> COLOR_CORRECTION_AVAILABLE_MODES =
+ new Key<int[]>("android.colorCorrection.availableModes", int[].class);
+
+ /**
* <p>List of auto-exposure antibanding modes for {@link CaptureRequest#CONTROL_AE_ANTIBANDING_MODE android.control.aeAntibandingMode} that are
* supported by this camera device.</p>
* <p>Not all of the auto-exposure anti-banding modes may be
diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java
index acb48f328f1a..86bbd4a57a63 100644
--- a/core/java/android/hardware/camera2/CameraMetadata.java
+++ b/core/java/android/hardware/camera2/CameraMetadata.java
@@ -2169,6 +2169,22 @@ public abstract class CameraMetadata<TKey> {
*/
public static final int COLOR_CORRECTION_MODE_HIGH_QUALITY = 2;
+ /**
+ * <p>Use
+ * {@link CaptureRequest#COLOR_CORRECTION_COLOR_TEMPERATURE android.colorCorrection.colorTemperature} and
+ * {@link CaptureRequest#COLOR_CORRECTION_COLOR_TINT android.colorCorrection.colorTint} to adjust the white balance based
+ * on correlated color temperature.</p>
+ * <p>If AWB is enabled with <code>{@link CaptureRequest#CONTROL_AWB_MODE android.control.awbMode} != OFF</code>, then
+ * CCT is ignored.</p>
+ *
+ * @see CaptureRequest#COLOR_CORRECTION_COLOR_TEMPERATURE
+ * @see CaptureRequest#COLOR_CORRECTION_COLOR_TINT
+ * @see CaptureRequest#CONTROL_AWB_MODE
+ * @see CaptureRequest#COLOR_CORRECTION_MODE
+ */
+ @FlaggedApi(Flags.FLAG_COLOR_TEMPERATURE)
+ public static final int COLOR_CORRECTION_MODE_CCT = 3;
+
//
// Enumeration values for CaptureRequest#COLOR_CORRECTION_ABERRATION_MODE
//
diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java
index a5c5a9952879..8142bbe9b838 100644
--- a/core/java/android/hardware/camera2/CaptureRequest.java
+++ b/core/java/android/hardware/camera2/CaptureRequest.java
@@ -1086,11 +1086,17 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
* <li>{@link #COLOR_CORRECTION_MODE_HIGH_QUALITY HIGH_QUALITY}</li>
* </ul>
*
+ * <p><b>Available values for this device:</b><br>
+ * Starting from API level 36, {@link CameraCharacteristics#COLOR_CORRECTION_AVAILABLE_MODES android.colorCorrection.availableModes}
+ * can be used to check the list of supported values. Prior to API level 36,
+ * TRANSFORM_MATRIX, HIGH_QUALITY, and FAST are guaranteed to be available
+ * as valid modes on devices that support this key.</p>
* <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
* <p><b>Full capability</b> -
* Present on all camera devices that report being {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL_FULL HARDWARE_LEVEL_FULL} devices in the
* {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL android.info.supportedHardwareLevel} key</p>
*
+ * @see CameraCharacteristics#COLOR_CORRECTION_AVAILABLE_MODES
* @see CaptureRequest#COLOR_CORRECTION_GAINS
* @see CaptureRequest#COLOR_CORRECTION_TRANSFORM
* @see CaptureRequest#CONTROL_AWB_MODE
@@ -1195,6 +1201,60 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
new Key<Integer>("android.colorCorrection.aberrationMode", int.class);
/**
+ * <p>Specifies the color temperature for CCT mode in Kelvin
+ * to adjust the white balance of the image.</p>
+ * <p>Sets the color temperature in Kelvin units for when
+ * {@link CaptureRequest#COLOR_CORRECTION_MODE android.colorCorrection.mode} is CCT to adjust the
+ * white balance of the image.</p>
+ * <p>If CCT mode is enabled without a requested color temperature,
+ * a default value will be set by the camera device. The default value can be
+ * retrieved by checking the corresponding capture result. Color temperatures
+ * requested outside the advertised {@link CameraCharacteristics#COLOR_CORRECTION_COLOR_TEMPERATURE_RANGE android.colorCorrection.colorTemperatureRange}
+ * will be clamped.</p>
+ * <p><b>Units</b>: Kelvin</p>
+ * <p><b>Range of valid values:</b><br>
+ * {@link CameraCharacteristics#COLOR_CORRECTION_COLOR_TEMPERATURE_RANGE android.colorCorrection.colorTemperatureRange}</p>
+ * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+ *
+ * @see CameraCharacteristics#COLOR_CORRECTION_COLOR_TEMPERATURE_RANGE
+ * @see CaptureRequest#COLOR_CORRECTION_MODE
+ */
+ @PublicKey
+ @NonNull
+ @FlaggedApi(Flags.FLAG_COLOR_TEMPERATURE)
+ public static final Key<Integer> COLOR_CORRECTION_COLOR_TEMPERATURE =
+ new Key<Integer>("android.colorCorrection.colorTemperature", int.class);
+
+ /**
+ * <p>Specifies the color tint for CCT mode to adjust the white
+ * balance of the image.</p>
+ * <p>Sets the color tint for when {@link CaptureRequest#COLOR_CORRECTION_MODE android.colorCorrection.mode}
+ * is CCT to adjust the white balance of the image.</p>
+ * <p>If CCT mode is enabled without a requested color tint,
+ * a default value will be set by the camera device. The default value can be
+ * retrieved by checking the corresponding capture result. Color tints requested
+ * outside the supported range will be clamped to the nearest limit (-50 or +50).</p>
+ * <p><b>Units</b>: D_uv defined as the distance from the Planckian locus on the CIE 1931 xy
+ * chromaticity diagram, with the range ±50 mapping to ±0.01 D_uv</p>
+ * <p><b>Range of valid values:</b><br>
+ * The supported range, -50 to +50, corresponds to a D_uv distance
+ * of ±0.01 below and above the Planckian locus. Some camera devices may have
+ * limitations to achieving the full ±0.01 D_uv range at some color temperatures
+ * (e.g., below 1500K). In these cases, the applied D_uv value may be clamped and
+ * the actual color tint will be reported in the {@link CaptureRequest#COLOR_CORRECTION_COLOR_TINT android.colorCorrection.colorTint}
+ * result.</p>
+ * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+ *
+ * @see CaptureRequest#COLOR_CORRECTION_COLOR_TINT
+ * @see CaptureRequest#COLOR_CORRECTION_MODE
+ */
+ @PublicKey
+ @NonNull
+ @FlaggedApi(Flags.FLAG_COLOR_TEMPERATURE)
+ public static final Key<Integer> COLOR_CORRECTION_COLOR_TINT =
+ new Key<Integer>("android.colorCorrection.colorTint", int.class);
+
+ /**
* <p>The desired setting for the camera device's auto-exposure
* algorithm's antibanding compensation.</p>
* <p>Some kinds of lighting fixtures, such as some fluorescent
diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java
index a6bdb3f1bb07..ae72ca40fc5a 100644
--- a/core/java/android/hardware/camera2/CaptureResult.java
+++ b/core/java/android/hardware/camera2/CaptureResult.java
@@ -487,11 +487,17 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> {
* <li>{@link #COLOR_CORRECTION_MODE_HIGH_QUALITY HIGH_QUALITY}</li>
* </ul>
*
+ * <p><b>Available values for this device:</b><br>
+ * Starting from API level 36, {@link CameraCharacteristics#COLOR_CORRECTION_AVAILABLE_MODES android.colorCorrection.availableModes}
+ * can be used to check the list of supported values. Prior to API level 36,
+ * TRANSFORM_MATRIX, HIGH_QUALITY, and FAST are guaranteed to be available
+ * as valid modes on devices that support this key.</p>
* <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
* <p><b>Full capability</b> -
* Present on all camera devices that report being {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL_FULL HARDWARE_LEVEL_FULL} devices in the
* {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL android.info.supportedHardwareLevel} key</p>
*
+ * @see CameraCharacteristics#COLOR_CORRECTION_AVAILABLE_MODES
* @see CaptureRequest#COLOR_CORRECTION_GAINS
* @see CaptureRequest#COLOR_CORRECTION_TRANSFORM
* @see CaptureRequest#CONTROL_AWB_MODE
@@ -596,6 +602,60 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> {
new Key<Integer>("android.colorCorrection.aberrationMode", int.class);
/**
+ * <p>Specifies the color temperature for CCT mode in Kelvin
+ * to adjust the white balance of the image.</p>
+ * <p>Sets the color temperature in Kelvin units for when
+ * {@link CaptureRequest#COLOR_CORRECTION_MODE android.colorCorrection.mode} is CCT to adjust the
+ * white balance of the image.</p>
+ * <p>If CCT mode is enabled without a requested color temperature,
+ * a default value will be set by the camera device. The default value can be
+ * retrieved by checking the corresponding capture result. Color temperatures
+ * requested outside the advertised {@link CameraCharacteristics#COLOR_CORRECTION_COLOR_TEMPERATURE_RANGE android.colorCorrection.colorTemperatureRange}
+ * will be clamped.</p>
+ * <p><b>Units</b>: Kelvin</p>
+ * <p><b>Range of valid values:</b><br>
+ * {@link CameraCharacteristics#COLOR_CORRECTION_COLOR_TEMPERATURE_RANGE android.colorCorrection.colorTemperatureRange}</p>
+ * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+ *
+ * @see CameraCharacteristics#COLOR_CORRECTION_COLOR_TEMPERATURE_RANGE
+ * @see CaptureRequest#COLOR_CORRECTION_MODE
+ */
+ @PublicKey
+ @NonNull
+ @FlaggedApi(Flags.FLAG_COLOR_TEMPERATURE)
+ public static final Key<Integer> COLOR_CORRECTION_COLOR_TEMPERATURE =
+ new Key<Integer>("android.colorCorrection.colorTemperature", int.class);
+
+ /**
+ * <p>Specifies the color tint for CCT mode to adjust the white
+ * balance of the image.</p>
+ * <p>Sets the color tint for when {@link CaptureRequest#COLOR_CORRECTION_MODE android.colorCorrection.mode}
+ * is CCT to adjust the white balance of the image.</p>
+ * <p>If CCT mode is enabled without a requested color tint,
+ * a default value will be set by the camera device. The default value can be
+ * retrieved by checking the corresponding capture result. Color tints requested
+ * outside the supported range will be clamped to the nearest limit (-50 or +50).</p>
+ * <p><b>Units</b>: D_uv defined as the distance from the Planckian locus on the CIE 1931 xy
+ * chromaticity diagram, with the range ±50 mapping to ±0.01 D_uv</p>
+ * <p><b>Range of valid values:</b><br>
+ * The supported range, -50 to +50, corresponds to a D_uv distance
+ * of ±0.01 below and above the Planckian locus. Some camera devices may have
+ * limitations to achieving the full ±0.01 D_uv range at some color temperatures
+ * (e.g., below 1500K). In these cases, the applied D_uv value may be clamped and
+ * the actual color tint will be reported in the {@link CaptureRequest#COLOR_CORRECTION_COLOR_TINT android.colorCorrection.colorTint}
+ * result.</p>
+ * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+ *
+ * @see CaptureRequest#COLOR_CORRECTION_COLOR_TINT
+ * @see CaptureRequest#COLOR_CORRECTION_MODE
+ */
+ @PublicKey
+ @NonNull
+ @FlaggedApi(Flags.FLAG_COLOR_TEMPERATURE)
+ public static final Key<Integer> COLOR_CORRECTION_COLOR_TINT =
+ new Key<Integer>("android.colorCorrection.colorTint", int.class);
+
+ /**
* <p>The desired setting for the camera device's auto-exposure
* algorithm's antibanding compensation.</p>
* <p>Some kinds of lighting fixtures, such as some fluorescent
diff --git a/core/java/android/hardware/camera2/params/OutputConfiguration.java b/core/java/android/hardware/camera2/params/OutputConfiguration.java
index 22dbf5bdde37..d38be9b7b694 100644
--- a/core/java/android/hardware/camera2/params/OutputConfiguration.java
+++ b/core/java/android/hardware/camera2/params/OutputConfiguration.java
@@ -1457,7 +1457,7 @@ public final class OutputConfiguration implements Parcelable {
/**
* Set the mirroring mode for a surface belonging to this OutputConfiguration
*
- * <p>This function is identical to {@link #setMirroMode(int)} if {@code surface} is
+ * <p>This function is identical to {@link #setMirrorMode(int)} if {@code surface} is
* the only surface belonging to this OutputConfiguration.</p>
*
* <p>If this OutputConfiguration contains a deferred surface, the application can either
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index b0ea92d140a5..a81bcbcce9c9 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -580,7 +580,7 @@ public final class DisplayManager {
EVENT_FLAG_DISPLAY_CONNECTION_CHANGED,
})
@Retention(RetentionPolicy.SOURCE)
- public @interface EventsMask {}
+ public @interface EventFlag {}
/**
* Event type for when a new display is added.
@@ -774,7 +774,7 @@ public final class DisplayManager {
* @param listener The listener to register.
* @param handler The handler on which the listener should be invoked, or null
* if the listener should be invoked on the calling thread's looper.
- * @param eventsMask A bitmask of the event types for which this listener is subscribed.
+ * @param eventFlagsMask A bitmask of the event types for which this listener is subscribed.
*
* @see #EVENT_FLAG_DISPLAY_ADDED
* @see #EVENT_FLAG_DISPLAY_CHANGED
@@ -786,8 +786,8 @@ public final class DisplayManager {
* @hide
*/
public void registerDisplayListener(@NonNull DisplayListener listener,
- @Nullable Handler handler, @EventsMask long eventsMask) {
- mGlobal.registerDisplayListener(listener, handler, eventsMask,
+ @Nullable Handler handler, @EventFlag long eventFlagsMask) {
+ mGlobal.registerDisplayListener(listener, handler, eventFlagsMask,
ActivityThread.currentPackageName());
}
diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java
index 6affd123bfde..3c6841cd8aeb 100644
--- a/core/java/android/hardware/display/DisplayManagerGlobal.java
+++ b/core/java/android/hardware/display/DisplayManagerGlobal.java
@@ -17,7 +17,7 @@
package android.hardware.display;
-import static android.hardware.display.DisplayManager.EventsMask;
+import static android.hardware.display.DisplayManager.EventFlag;
import static android.view.Display.HdrCapabilities.HdrType;
import android.Manifest;
@@ -130,7 +130,7 @@ public final class DisplayManagerGlobal {
private final IDisplayManager mDm;
private DisplayManagerCallback mCallback;
- private @EventsMask long mRegisteredEventsMask = 0;
+ private @EventFlag long mRegisteredEventFlagsMask = 0;
private final CopyOnWriteArrayList<DisplayListenerDelegate> mDisplayListeners =
new CopyOnWriteArrayList<>();
@@ -346,10 +346,11 @@ public final class DisplayManagerGlobal {
* @param packageName of the calling package.
*/
public void registerDisplayListener(@NonNull DisplayListener listener,
- @Nullable Handler handler, @EventsMask long eventsMask, String packageName) {
+ @Nullable Handler handler, @EventFlag long eventFlagsMask,
+ String packageName) {
Looper looper = getLooperForHandler(handler);
Handler springBoard = new Handler(looper);
- registerDisplayListener(listener, new HandlerExecutor(springBoard), eventsMask,
+ registerDisplayListener(listener, new HandlerExecutor(springBoard), eventFlagsMask,
packageName);
}
@@ -358,32 +359,32 @@ public final class DisplayManagerGlobal {
*
* @param listener The listener that will be called when display changes occur.
* @param executor Executor for the thread that will be receiving the callbacks. Cannot be null.
- * @param eventsMask Mask of events to be listened to.
+ * @param eventFlagsMask Flag of events to be listened to.
* @param packageName of the calling package.
*/
public void registerDisplayListener(@NonNull DisplayListener listener,
- @NonNull Executor executor, @EventsMask long eventsMask, String packageName) {
+ @NonNull Executor executor, @EventFlag long eventFlagsMask, String packageName) {
if (listener == null) {
throw new IllegalArgumentException("listener must not be null");
}
- if (eventsMask == 0) {
+ if (eventFlagsMask == 0) {
throw new IllegalArgumentException("The set of events to listen to must not be empty.");
}
if (extraLogging()) {
Slog.i(TAG, "Registering Display Listener: "
- + Long.toBinaryString(eventsMask) + ", packageName: " + packageName);
+ + Long.toBinaryString(eventFlagsMask) + ", packageName: " + packageName);
}
synchronized (mLock) {
int index = findDisplayListenerLocked(listener);
if (index < 0) {
- mDisplayListeners.add(new DisplayListenerDelegate(listener, executor, eventsMask,
- packageName));
+ mDisplayListeners.add(new DisplayListenerDelegate(listener, executor,
+ eventFlagsMask, packageName));
registerCallbackIfNeededLocked();
} else {
- mDisplayListeners.get(index).setEventsMask(eventsMask);
+ mDisplayListeners.get(index).setEventFlagsMask(eventFlagsMask);
}
updateCallbackIfNeededLocked();
maybeLogAllDisplayListeners();
@@ -455,12 +456,12 @@ public final class DisplayManagerGlobal {
return -1;
}
- @EventsMask
- private int calculateEventsMaskLocked() {
+ @EventFlag
+ private int calculateEventFlagsMaskLocked() {
int mask = 0;
final int numListeners = mDisplayListeners.size();
for (int i = 0; i < numListeners; i++) {
- mask |= mDisplayListeners.get(i).mEventsMask;
+ mask |= mDisplayListeners.get(i).mEventFlagsMask;
}
if (mDispatchNativeCallbacks) {
mask |= DisplayManager.EVENT_FLAG_DISPLAY_ADDED
@@ -478,14 +479,14 @@ public final class DisplayManagerGlobal {
}
private void updateCallbackIfNeededLocked() {
- int mask = calculateEventsMaskLocked();
+ int mask = calculateEventFlagsMaskLocked();
if (DEBUG) {
- Log.d(TAG, "Mask for listener: " + mask);
+ Log.d(TAG, "Flag for listener: " + mask);
}
- if (mask != mRegisteredEventsMask) {
+ if (mask != mRegisteredEventFlagsMask) {
try {
mDm.registerCallbackWithEventMask(mCallback, mask);
- mRegisteredEventsMask = mask;
+ mRegisteredEventFlagsMask = mask;
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
@@ -1276,7 +1277,7 @@ public final class DisplayManagerGlobal {
private static final class DisplayListenerDelegate {
public final DisplayListener mListener;
- public volatile long mEventsMask;
+ public volatile long mEventFlagsMask;
private final DisplayInfo mDisplayInfo = new DisplayInfo();
private final Executor mExecutor;
@@ -1284,10 +1285,10 @@ public final class DisplayManagerGlobal {
private final String mPackageName;
DisplayListenerDelegate(DisplayListener listener, @NonNull Executor executor,
- @EventsMask long eventsMask, String packageName) {
+ @EventFlag long eventFlag, String packageName) {
mExecutor = executor;
mListener = listener;
- mEventsMask = eventsMask;
+ mEventFlagsMask = eventFlag;
mPackageName = packageName;
}
@@ -1309,16 +1310,16 @@ public final class DisplayManagerGlobal {
mGenerationId.incrementAndGet();
}
- void setEventsMask(@EventsMask long newEventsMask) {
- mEventsMask = newEventsMask;
+ void setEventFlagsMask(@EventFlag long newEventsFlag) {
+ mEventFlagsMask = newEventsFlag;
}
- private void handleDisplayEventInner(int displayId, @DisplayEvent int event,
+ private void handleDisplayEventInner(int displayId, @DisplayEvent int eventFlagsMask,
@Nullable DisplayInfo info, boolean forceUpdate) {
if (extraLogging()) {
- Slog.i(TAG, "DLD(" + eventToString(event)
+ Slog.i(TAG, "DLD(" + eventToString(eventFlagsMask)
+ ", display=" + displayId
- + ", mEventsMask=" + Long.toBinaryString(mEventsMask)
+ + ", mEventsFlagMask=" + Long.toBinaryString(mEventFlagsMask)
+ ", mPackageName=" + mPackageName
+ ", displayInfo=" + info
+ ", listener=" + mListener.getClass() + ")");
@@ -1326,18 +1327,18 @@ public final class DisplayManagerGlobal {
if (DEBUG) {
Trace.beginSection(
TextUtils.trimToSize(
- "DLD(" + eventToString(event)
+ "DLD(" + eventToString(eventFlagsMask)
+ ", display=" + displayId
+ ", listener=" + mListener.getClass() + ")", 127));
}
- switch (event) {
+ switch (eventFlagsMask) {
case EVENT_DISPLAY_ADDED:
- if ((mEventsMask & DisplayManager.EVENT_FLAG_DISPLAY_ADDED) != 0) {
+ if ((mEventFlagsMask & DisplayManager.EVENT_FLAG_DISPLAY_ADDED) != 0) {
mListener.onDisplayAdded(displayId);
}
break;
case EVENT_DISPLAY_CHANGED:
- if ((mEventsMask & DisplayManager.EVENT_FLAG_DISPLAY_CHANGED) != 0) {
+ if ((mEventFlagsMask & DisplayManager.EVENT_FLAG_DISPLAY_CHANGED) != 0) {
if (info != null && (forceUpdate || !info.equals(mDisplayInfo))) {
if (extraLogging()) {
Slog.i(TAG, "Sending onDisplayChanged: Display Changed. Info: "
@@ -1349,27 +1350,29 @@ public final class DisplayManagerGlobal {
}
break;
case EVENT_DISPLAY_BRIGHTNESS_CHANGED:
- if ((mEventsMask & DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS) != 0) {
+ if ((mEventFlagsMask & DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS) != 0) {
mListener.onDisplayChanged(displayId);
}
break;
case EVENT_DISPLAY_REMOVED:
- if ((mEventsMask & DisplayManager.EVENT_FLAG_DISPLAY_REMOVED) != 0) {
+ if ((mEventFlagsMask & DisplayManager.EVENT_FLAG_DISPLAY_REMOVED) != 0) {
mListener.onDisplayRemoved(displayId);
}
break;
case EVENT_DISPLAY_HDR_SDR_RATIO_CHANGED:
- if ((mEventsMask & DisplayManager.EVENT_FLAG_HDR_SDR_RATIO_CHANGED) != 0) {
+ if ((mEventFlagsMask & DisplayManager.EVENT_FLAG_HDR_SDR_RATIO_CHANGED) != 0) {
mListener.onDisplayChanged(displayId);
}
break;
case EVENT_DISPLAY_CONNECTED:
- if ((mEventsMask & DisplayManager.EVENT_FLAG_DISPLAY_CONNECTION_CHANGED) != 0) {
+ if ((mEventFlagsMask & DisplayManager.EVENT_FLAG_DISPLAY_CONNECTION_CHANGED)
+ != 0) {
mListener.onDisplayConnected(displayId);
}
break;
case EVENT_DISPLAY_DISCONNECTED:
- if ((mEventsMask & DisplayManager.EVENT_FLAG_DISPLAY_CONNECTION_CHANGED) != 0) {
+ if ((mEventFlagsMask & DisplayManager.EVENT_FLAG_DISPLAY_CONNECTION_CHANGED)
+ != 0) {
mListener.onDisplayDisconnected(displayId);
}
break;
@@ -1381,7 +1384,7 @@ public final class DisplayManagerGlobal {
@Override
public String toString() {
- return "mask: {" + mEventsMask + "}, for " + mListener.getClass();
+ return "mEventFlagsMask: {" + mEventFlagsMask + "}, for " + mListener.getClass();
}
}
diff --git a/core/java/android/hardware/display/VirtualDisplayConfig.java b/core/java/android/hardware/display/VirtualDisplayConfig.java
index b0994e6145ef..49944c76eb99 100644
--- a/core/java/android/hardware/display/VirtualDisplayConfig.java
+++ b/core/java/android/hardware/display/VirtualDisplayConfig.java
@@ -60,6 +60,7 @@ public final class VirtualDisplayConfig implements Parcelable {
private final float mRequestedRefreshRate;
private final boolean mIsHomeSupported;
private final DisplayCutout mDisplayCutout;
+ private final boolean mIgnoreActivitySizeRestrictions;
private VirtualDisplayConfig(
@NonNull String name,
@@ -74,7 +75,8 @@ public final class VirtualDisplayConfig implements Parcelable {
@NonNull ArraySet<String> displayCategories,
float requestedRefreshRate,
boolean isHomeSupported,
- @Nullable DisplayCutout displayCutout) {
+ @Nullable DisplayCutout displayCutout,
+ boolean ignoreActivitySizeRestrictions) {
mName = name;
mWidth = width;
mHeight = height;
@@ -88,6 +90,7 @@ public final class VirtualDisplayConfig implements Parcelable {
mRequestedRefreshRate = requestedRefreshRate;
mIsHomeSupported = isHomeSupported;
mDisplayCutout = displayCutout;
+ mIgnoreActivitySizeRestrictions = ignoreActivitySizeRestrictions;
}
/**
@@ -193,6 +196,20 @@ public final class VirtualDisplayConfig implements Parcelable {
}
/**
+ * Whether this virtual display ignores fixed orientation, aspect ratio and resizability
+ * of apps.
+ *
+ * @see Builder#setIgnoreActivitySizeRestrictions(boolean)
+ * @hide
+ */
+ @FlaggedApi(com.android.window.flags.Flags.FLAG_VDM_FORCE_APP_UNIVERSAL_RESIZABLE_API)
+ @SystemApi
+ public boolean isIgnoreActivitySizeRestrictions() {
+ return mIgnoreActivitySizeRestrictions
+ && com.android.window.flags.Flags.vdmForceAppUniversalResizableApi();
+ }
+
+ /**
* Returns the display categories.
*
* @see Builder#setDisplayCategories
@@ -227,6 +244,7 @@ public final class VirtualDisplayConfig implements Parcelable {
dest.writeFloat(mRequestedRefreshRate);
dest.writeBoolean(mIsHomeSupported);
DisplayCutout.ParcelableWrapper.writeCutoutToParcel(mDisplayCutout, dest, flags);
+ dest.writeBoolean(mIgnoreActivitySizeRestrictions);
}
@Override
@@ -253,6 +271,7 @@ public final class VirtualDisplayConfig implements Parcelable {
&& Objects.equals(mDisplayCategories, that.mDisplayCategories)
&& mRequestedRefreshRate == that.mRequestedRefreshRate
&& mIsHomeSupported == that.mIsHomeSupported
+ && mIgnoreActivitySizeRestrictions == that.mIgnoreActivitySizeRestrictions
&& Objects.equals(mDisplayCutout, that.mDisplayCutout);
}
@@ -261,7 +280,8 @@ public final class VirtualDisplayConfig implements Parcelable {
int hashCode = Objects.hash(
mName, mWidth, mHeight, mDensityDpi, mFlags, mSurface, mUniqueId,
mDisplayIdToMirror, mWindowManagerMirroringEnabled, mDisplayCategories,
- mRequestedRefreshRate, mIsHomeSupported, mDisplayCutout);
+ mRequestedRefreshRate, mIsHomeSupported, mDisplayCutout,
+ mIgnoreActivitySizeRestrictions);
return hashCode;
}
@@ -282,6 +302,7 @@ public final class VirtualDisplayConfig implements Parcelable {
+ " mRequestedRefreshRate=" + mRequestedRefreshRate
+ " mIsHomeSupported=" + mIsHomeSupported
+ " mDisplayCutout=" + mDisplayCutout
+ + " mIgnoreActivitySizeRestrictions=" + mIgnoreActivitySizeRestrictions
+ ")";
}
@@ -299,6 +320,7 @@ public final class VirtualDisplayConfig implements Parcelable {
mRequestedRefreshRate = in.readFloat();
mIsHomeSupported = in.readBoolean();
mDisplayCutout = DisplayCutout.ParcelableWrapper.readCutoutFromParcel(in);
+ mIgnoreActivitySizeRestrictions = in.readBoolean();
}
@NonNull
@@ -332,6 +354,7 @@ public final class VirtualDisplayConfig implements Parcelable {
private float mRequestedRefreshRate = 0.0f;
private boolean mIsHomeSupported = false;
private DisplayCutout mDisplayCutout = null;
+ private boolean mIgnoreActivitySizeRestrictions = false;
/**
* Creates a new Builder.
@@ -506,6 +529,24 @@ public final class VirtualDisplayConfig implements Parcelable {
}
/**
+ * Sets whether this display ignores fixed orientation, aspect ratio and resizability
+ * of apps.
+ *
+ * <p>Note: setting to {@code true} requires the display to have
+ * {@link DisplayManager#VIRTUAL_DISPLAY_FLAG_TRUSTED}. If this is false, this property
+ * is ignored.</p>
+ *
+ * @hide
+ */
+ @FlaggedApi(com.android.window.flags.Flags.FLAG_VDM_FORCE_APP_UNIVERSAL_RESIZABLE_API)
+ @SystemApi
+ @NonNull
+ public Builder setIgnoreActivitySizeRestrictions(boolean enabled) {
+ mIgnoreActivitySizeRestrictions = enabled;
+ return this;
+ }
+
+ /**
* Builds the {@link VirtualDisplayConfig} instance.
*/
@NonNull
@@ -523,7 +564,8 @@ public final class VirtualDisplayConfig implements Parcelable {
mDisplayCategories,
mRequestedRefreshRate,
mIsHomeSupported,
- mDisplayCutout);
+ mDisplayCutout,
+ mIgnoreActivitySizeRestrictions);
}
}
}
diff --git a/core/java/android/hardware/input/KeyGestureEvent.java b/core/java/android/hardware/input/KeyGestureEvent.java
index 0f290d90770f..9d42b67bf5a8 100644
--- a/core/java/android/hardware/input/KeyGestureEvent.java
+++ b/core/java/android/hardware/input/KeyGestureEvent.java
@@ -113,6 +113,10 @@ public final class KeyGestureEvent {
public static final int KEY_GESTURE_TYPE_TOGGLE_BOUNCE_KEYS = 65;
public static final int KEY_GESTURE_TYPE_TOGGLE_SLOW_KEYS = 66;
public static final int KEY_GESTURE_TYPE_TOGGLE_MOUSE_KEYS = 67;
+ public static final int KEY_GESTURE_TYPE_SNAP_LEFT_FREEFORM_WINDOW = 68;
+ public static final int KEY_GESTURE_TYPE_SNAP_RIGHT_FREEFORM_WINDOW = 69;
+ public static final int KEY_GESTURE_TYPE_MAXIMIZE_FREEFORM_WINDOW = 70;
+ public static final int KEY_GESTURE_TYPE_RESTORE_FREEFORM_WINDOW_SIZE = 71;
public static final int FLAG_CANCELLED = 1;
@@ -194,7 +198,11 @@ public final class KeyGestureEvent {
KEY_GESTURE_TYPE_TOGGLE_STICKY_KEYS,
KEY_GESTURE_TYPE_TOGGLE_BOUNCE_KEYS,
KEY_GESTURE_TYPE_TOGGLE_SLOW_KEYS,
- KEY_GESTURE_TYPE_TOGGLE_MOUSE_KEYS
+ KEY_GESTURE_TYPE_TOGGLE_MOUSE_KEYS,
+ KEY_GESTURE_TYPE_SNAP_LEFT_FREEFORM_WINDOW,
+ KEY_GESTURE_TYPE_SNAP_RIGHT_FREEFORM_WINDOW,
+ KEY_GESTURE_TYPE_MAXIMIZE_FREEFORM_WINDOW,
+ KEY_GESTURE_TYPE_RESTORE_FREEFORM_WINDOW_SIZE,
})
@Retention(RetentionPolicy.SOURCE)
public @interface KeyGestureType {
@@ -541,6 +549,14 @@ public final class KeyGestureEvent {
return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__DESKTOP_MODE;
case KEY_GESTURE_TYPE_MULTI_WINDOW_NAVIGATION:
return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__MULTI_WINDOW_NAVIGATION;
+ case KEY_GESTURE_TYPE_SNAP_LEFT_FREEFORM_WINDOW:
+ return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__SNAP_LEFT_FREEFORM_WINDOW;
+ case KEY_GESTURE_TYPE_SNAP_RIGHT_FREEFORM_WINDOW:
+ return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__SNAP_RIGHT_FREEFORM_WINDOW;
+ case KEY_GESTURE_TYPE_MAXIMIZE_FREEFORM_WINDOW:
+ return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__MAXIMIZE_FREEFORM_WINDOW;
+ case KEY_GESTURE_TYPE_RESTORE_FREEFORM_WINDOW_SIZE:
+ return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__RESTORE_FREEFORM_WINDOW_SIZE;
default:
return LOG_EVENT_UNSPECIFIED;
}
@@ -749,6 +765,14 @@ public final class KeyGestureEvent {
return "KEY_GESTURE_TYPE_TOGGLE_SLOW_KEYS";
case KEY_GESTURE_TYPE_TOGGLE_MOUSE_KEYS:
return "KEY_GESTURE_TYPE_TOGGLE_MOUSE_KEYS";
+ case KEY_GESTURE_TYPE_SNAP_LEFT_FREEFORM_WINDOW:
+ return "KEY_GESTURE_TYPE_SNAP_LEFT_FREEFORM_WINDOW";
+ case KEY_GESTURE_TYPE_SNAP_RIGHT_FREEFORM_WINDOW:
+ return "KEY_GESTURE_TYPE_SNAP_RIGHT_FREEFORM_WINDOW";
+ case KEY_GESTURE_TYPE_MAXIMIZE_FREEFORM_WINDOW:
+ return "KEY_GESTURE_TYPE_SNAP_RIGHT_FREEFORM_WINDOW";
+ case KEY_GESTURE_TYPE_RESTORE_FREEFORM_WINDOW_SIZE:
+ return "KEY_GESTURE_TYPE_RESTORE_FREEFORM_WINDOW_SIZE";
default:
return Integer.toHexString(value);
}
diff --git a/core/java/android/os/CombinedMessageQueue/MessageQueue.java b/core/java/android/os/CombinedMessageQueue/MessageQueue.java
index 3e5ac6ff5a4a..9806ab25745c 100644
--- a/core/java/android/os/CombinedMessageQueue/MessageQueue.java
+++ b/core/java/android/os/CombinedMessageQueue/MessageQueue.java
@@ -76,19 +76,14 @@ public final class MessageQueue {
private final ArrayList<IdleHandler> mIdleHandlers = new ArrayList<IdleHandler>();
private SparseArray<FileDescriptorRecord> mFileDescriptorRecords;
private IdleHandler[] mPendingIdleHandlers;
- private boolean mLegacyQuitting;
+ private boolean mQuitting;
// Indicates whether next() is blocked waiting in pollOnce() with a non-zero timeout.
- private boolean mLegacyBlocked;
+ private boolean mBlocked;
// Tracks the number of async message. We use this in enqueueMessage() to avoid searching the
// queue for async messages when inserting a message at the tail.
- private int mLegacyAsyncMessageCount;
-
- // The next barrier token.
- // Barriers are indicated by messages with a null target whose arg1 field carries the token.
- @UnsupportedAppUsage
- private int mLegacyNextBarrierToken;
+ private int mAsyncMessageCount;
/*
* Select between two implementations of message queue. The legacy implementation is used
@@ -139,7 +134,7 @@ public final class MessageQueue {
}
}
- private class MatchDeliverableMessages extends MessageCompare {
+ private static final class MatchDeliverableMessages extends MessageCompare {
@Override
public boolean compareMessage(Message m, Handler h, int what, Object object, Runnable r,
long when) {
@@ -266,8 +261,8 @@ public final class MessageQueue {
private boolean isPollingLocked() {
// If the loop is quitting then it must not be idling.
- // We can assume mPtr != 0 when mLegacyQuitting is false.
- return !mLegacyQuitting && nativeIsPolling(mPtr);
+ // We can assume mPtr != 0 when mQuitting is false.
+ return !mQuitting && nativeIsPolling(mPtr);
}
/**
@@ -748,7 +743,7 @@ public final class MessageQueue {
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// Got a message.
- mLegacyBlocked = false;
+ mBlocked = false;
if (prevMsg != null) {
prevMsg.next = msg.next;
if (prevMsg.next == null) {
@@ -764,7 +759,7 @@ public final class MessageQueue {
if (DEBUG) Log.v(TAG_L, "Returning message: " + msg);
msg.markInUse();
if (msg.isAsynchronous()) {
- mLegacyAsyncMessageCount--;
+ mAsyncMessageCount--;
}
if (TRACE) {
Trace.setCounter("MQ.Delivered", mMessagesDelivered.incrementAndGet());
@@ -777,7 +772,7 @@ public final class MessageQueue {
}
// Process the quit message now that all pending messages have been handled.
- if (mLegacyQuitting) {
+ if (mQuitting) {
dispose();
return null;
}
@@ -791,7 +786,7 @@ public final class MessageQueue {
}
if (pendingIdleHandlerCount <= 0) {
// No idle handlers to run. Loop and wait some more.
- mLegacyBlocked = true;
+ mBlocked = true;
continue;
}
@@ -850,10 +845,10 @@ public final class MessageQueue {
}
} else {
synchronized (this) {
- if (mLegacyQuitting) {
+ if (mQuitting) {
return;
}
- mLegacyQuitting = true;
+ mQuitting = true;
if (safe) {
removeAllFutureMessagesLocked();
@@ -861,7 +856,7 @@ public final class MessageQueue {
removeAllMessagesLocked();
}
- // We can assume mPtr != 0 because mLegacyQuitting was previously false.
+ // We can assume mPtr != 0 because mQuitting was previously false.
nativeWake(mPtr);
}
}
@@ -900,7 +895,12 @@ public final class MessageQueue {
// Enqueue a new sync barrier token.
// We don't need to wake the queue because the purpose of a barrier is to stall it.
if (sForceConcurrent) {
- final int token = mNextBarrierToken.getAndIncrement();
+ final int token = mNextBarrierTokenAtomic.getAndIncrement();
+
+ // b/376573804: apps and tests may expect to be able to use reflection
+ // to read this value. Make some effort to support this legacy use case.
+ mNextBarrierToken = token + 1;
+
final Message msg = Message.obtain();
msg.markInUse();
@@ -915,7 +915,7 @@ public final class MessageQueue {
}
synchronized (this) {
- final int token = mLegacyNextBarrierToken++;
+ final int token = mNextBarrierToken++;
final Message msg = Message.obtain();
msg.markInUse();
msg.when = when;
@@ -954,7 +954,7 @@ public final class MessageQueue {
}
}
- private class MatchBarrierToken extends MessageCompare {
+ private static final class MatchBarrierToken extends MessageCompare {
int mBarrierToken;
MatchBarrierToken(int token) {
@@ -1042,8 +1042,8 @@ public final class MessageQueue {
p.recycleUnchecked();
// If the loop is quitting then it is already awake.
- // We can assume mPtr != 0 when mLegacyQuitting is false.
- if (needWake && !mLegacyQuitting) {
+ // We can assume mPtr != 0 when mQuitting is false.
+ if (needWake && !mQuitting) {
nativeWake(mPtr);
}
}
@@ -1067,7 +1067,7 @@ public final class MessageQueue {
throw new IllegalStateException(msg + " This message is already in use.");
}
- if (mLegacyQuitting) {
+ if (mQuitting) {
IllegalStateException e = new IllegalStateException(
msg.target + " sending message to a Handler on a dead thread");
Log.w(TAG_L, e.getMessage(), e);
@@ -1083,7 +1083,7 @@ public final class MessageQueue {
// New head, wake up the event queue if blocked.
msg.next = p;
mMessages = msg;
- needWake = mLegacyBlocked;
+ needWake = mBlocked;
if (p == null) {
mLast = mMessages;
}
@@ -1091,14 +1091,14 @@ public final class MessageQueue {
// Message is to be inserted at tail or middle of queue. Usually we don't have to
// wake up the event queue unless there is a barrier at the head of the queue and
// the message is the earliest asynchronous message in the queue.
- needWake = mLegacyBlocked && p.target == null && msg.isAsynchronous();
+ needWake = mBlocked && p.target == null && msg.isAsynchronous();
// For readability, we split this portion of the function into two blocks based on
// whether tail tracking is enabled. This has a minor implication for the case
// where tail tracking is disabled. See the comment below.
if (Flags.messageQueueTailTracking()) {
if (when >= mLast.when) {
- needWake = needWake && mLegacyAsyncMessageCount == 0;
+ needWake = needWake && mAsyncMessageCount == 0;
msg.next = null;
mLast.next = msg;
mLast = msg;
@@ -1156,10 +1156,10 @@ public final class MessageQueue {
}
if (msg.isAsynchronous()) {
- mLegacyAsyncMessageCount++;
+ mAsyncMessageCount++;
}
- // We can assume mPtr != 0 because mLegacyQuitting is false.
+ // We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
nativeWake(mPtr);
}
@@ -1167,7 +1167,7 @@ public final class MessageQueue {
return true;
}
- private static class MatchHandlerWhatAndObject extends MessageCompare {
+ private static final class MatchHandlerWhatAndObject extends MessageCompare {
@Override
public boolean compareMessage(Message m, Handler h, int what, Object object, Runnable r,
long when) {
@@ -1199,7 +1199,7 @@ public final class MessageQueue {
}
}
- private static class MatchHandlerWhatAndObjectEquals extends MessageCompare {
+ private static final class MatchHandlerWhatAndObjectEquals extends MessageCompare {
@Override
public boolean compareMessage(Message m, Handler h, int what, Object object, Runnable r,
long when) {
@@ -1232,7 +1232,7 @@ public final class MessageQueue {
}
}
- private static class MatchHandlerRunnableAndObject extends MessageCompare {
+ private static final class MatchHandlerRunnableAndObject extends MessageCompare {
@Override
public boolean compareMessage(Message m, Handler h, int what, Object object, Runnable r,
long when) {
@@ -1266,7 +1266,7 @@ public final class MessageQueue {
}
}
- private static class MatchHandler extends MessageCompare {
+ private static final class MatchHandler extends MessageCompare {
@Override
public boolean compareMessage(Message m, Handler h, int what, Object object, Runnable r,
long when) {
@@ -1313,7 +1313,7 @@ public final class MessageQueue {
Message n = p.next;
mMessages = n;
if (p.isAsynchronous()) {
- mLegacyAsyncMessageCount--;
+ mAsyncMessageCount--;
}
p.recycleUnchecked();
p = n;
@@ -1331,7 +1331,7 @@ public final class MessageQueue {
&& (object == null || n.obj == object)) {
Message nn = n.next;
if (n.isAsynchronous()) {
- mLegacyAsyncMessageCount--;
+ mAsyncMessageCount--;
}
n.recycleUnchecked();
p.next = nn;
@@ -1365,7 +1365,7 @@ public final class MessageQueue {
Message n = p.next;
mMessages = n;
if (p.isAsynchronous()) {
- mLegacyAsyncMessageCount--;
+ mAsyncMessageCount--;
}
p.recycleUnchecked();
p = n;
@@ -1383,7 +1383,7 @@ public final class MessageQueue {
&& (object == null || object.equals(n.obj))) {
Message nn = n.next;
if (n.isAsynchronous()) {
- mLegacyAsyncMessageCount--;
+ mAsyncMessageCount--;
}
n.recycleUnchecked();
p.next = nn;
@@ -1416,7 +1416,7 @@ public final class MessageQueue {
Message n = p.next;
mMessages = n;
if (p.isAsynchronous()) {
- mLegacyAsyncMessageCount--;
+ mAsyncMessageCount--;
}
p.recycleUnchecked();
p = n;
@@ -1434,7 +1434,7 @@ public final class MessageQueue {
&& (object == null || n.obj == object)) {
Message nn = n.next;
if (n.isAsynchronous()) {
- mLegacyAsyncMessageCount--;
+ mAsyncMessageCount--;
}
n.recycleUnchecked();
p.next = nn;
@@ -1449,7 +1449,7 @@ public final class MessageQueue {
}
}
- private static class MatchHandlerRunnableAndObjectEquals extends MessageCompare {
+ private static final class MatchHandlerRunnableAndObjectEquals extends MessageCompare {
@Override
public boolean compareMessage(Message m, Handler h, int what, Object object, Runnable r,
long when) {
@@ -1479,7 +1479,7 @@ public final class MessageQueue {
Message n = p.next;
mMessages = n;
if (p.isAsynchronous()) {
- mLegacyAsyncMessageCount--;
+ mAsyncMessageCount--;
}
p.recycleUnchecked();
p = n;
@@ -1497,7 +1497,7 @@ public final class MessageQueue {
&& (object == null || object.equals(n.obj))) {
Message nn = n.next;
if (n.isAsynchronous()) {
- mLegacyAsyncMessageCount--;
+ mAsyncMessageCount--;
}
n.recycleUnchecked();
p.next = nn;
@@ -1512,7 +1512,7 @@ public final class MessageQueue {
}
}
- private static class MatchHandlerAndObject extends MessageCompare {
+ private static final class MatchHandlerAndObject extends MessageCompare {
@Override
public boolean compareMessage(Message m, Handler h, int what, Object object, Runnable r,
long when) {
@@ -1541,7 +1541,7 @@ public final class MessageQueue {
Message n = p.next;
mMessages = n;
if (p.isAsynchronous()) {
- mLegacyAsyncMessageCount--;
+ mAsyncMessageCount--;
}
p.recycleUnchecked();
p = n;
@@ -1558,7 +1558,7 @@ public final class MessageQueue {
if (n.target == h && (object == null || n.obj == object)) {
Message nn = n.next;
if (n.isAsynchronous()) {
- mLegacyAsyncMessageCount--;
+ mAsyncMessageCount--;
}
n.recycleUnchecked();
p.next = nn;
@@ -1573,7 +1573,7 @@ public final class MessageQueue {
}
}
- private static class MatchHandlerAndObjectEquals extends MessageCompare {
+ private static final class MatchHandlerAndObjectEquals extends MessageCompare {
@Override
public boolean compareMessage(Message m, Handler h, int what, Object object, Runnable r,
long when) {
@@ -1603,7 +1603,7 @@ public final class MessageQueue {
Message n = p.next;
mMessages = n;
if (p.isAsynchronous()) {
- mLegacyAsyncMessageCount--;
+ mAsyncMessageCount--;
}
p.recycleUnchecked();
p = n;
@@ -1620,7 +1620,7 @@ public final class MessageQueue {
if (n.target == h && (object == null || object.equals(n.obj))) {
Message nn = n.next;
if (n.isAsynchronous()) {
- mLegacyAsyncMessageCount--;
+ mAsyncMessageCount--;
}
n.recycleUnchecked();
p.next = nn;
@@ -1644,7 +1644,7 @@ public final class MessageQueue {
}
mMessages = null;
mLast = null;
- mLegacyAsyncMessageCount = 0;
+ mAsyncMessageCount = 0;
}
private void removeAllFutureMessagesLocked() {
@@ -1672,7 +1672,7 @@ public final class MessageQueue {
p = n;
n = p.next;
if (p.isAsynchronous()) {
- mLegacyAsyncMessageCount--;
+ mAsyncMessageCount--;
}
p.recycleUnchecked();
} while (n != null);
@@ -1680,7 +1680,7 @@ public final class MessageQueue {
}
}
- private static class MatchAllMessages extends MessageCompare {
+ private static final class MatchAllMessages extends MessageCompare {
@Override
public boolean compareMessage(Message m, Handler h, int what, Object object, Runnable r,
long when) {
@@ -1692,7 +1692,7 @@ public final class MessageQueue {
findOrRemoveMessages(null, -1, null, null, 0, mMatchAllMessages, true);
}
- private static class MatchAllFutureMessages extends MessageCompare {
+ private static final class MatchAllFutureMessages extends MessageCompare {
@Override
public boolean compareMessage(Message m, Handler h, int what, Object object, Runnable r,
long when) {
@@ -1780,7 +1780,7 @@ public final class MessageQueue {
n++;
}
pw.println(prefix + "(Total messages: " + n + ", polling=" + isPollingLocked()
- + ", quitting=" + mLegacyQuitting + ")");
+ + ", quitting=" + mQuitting + ")");
}
}
@@ -1824,7 +1824,7 @@ public final class MessageQueue {
msg.dumpDebug(proto, MessageQueueProto.MESSAGES);
}
proto.write(MessageQueueProto.IS_POLLING_LOCKED, isPollingLocked());
- proto.write(MessageQueueProto.IS_QUITTING, mLegacyQuitting);
+ proto.write(MessageQueueProto.IS_QUITTING, mQuitting);
}
proto.end(messageQueueToken);
}
@@ -2292,7 +2292,11 @@ public final class MessageQueue {
// The next barrier token.
// Barriers are indicated by messages with a null target whose arg1 field carries the token.
- private final AtomicInteger mNextBarrierToken = new AtomicInteger(1);
+ private final AtomicInteger mNextBarrierTokenAtomic = new AtomicInteger(1);
+
+ // Must retain this for compatibility reasons.
+ @UnsupportedAppUsage
+ private int mNextBarrierToken;
/* Protects mNextIsDrainingStack */
private final ReentrantLock mDrainingLock = new ReentrantLock();
diff --git a/core/java/android/os/ConcurrentMessageQueue/MessageQueue.java b/core/java/android/os/ConcurrentMessageQueue/MessageQueue.java
index 0efa02f24af5..9db88d17614b 100644
--- a/core/java/android/os/ConcurrentMessageQueue/MessageQueue.java
+++ b/core/java/android/os/ConcurrentMessageQueue/MessageQueue.java
@@ -382,7 +382,7 @@ public final class MessageQueue {
}
}
- private class MatchDeliverableMessages extends MessageCompare {
+ private static final class MatchDeliverableMessages extends MessageCompare {
@Override
public boolean compareMessage(Message m, Handler h, int what, Object object, Runnable r,
long when) {
@@ -984,7 +984,7 @@ public final class MessageQueue {
return token;
}
- private class MatchBarrierToken extends MessageCompare {
+ private static final class MatchBarrierToken extends MessageCompare {
int mBarrierToken;
MatchBarrierToken(int token) {
@@ -1165,7 +1165,7 @@ public final class MessageQueue {
return foundInStack || foundInQueue;
}
- private static class MatchHandlerWhatAndObject extends MessageCompare {
+ private static final class MatchHandlerWhatAndObject extends MessageCompare {
@Override
public boolean compareMessage(Message m, Handler h, int what, Object object, Runnable r,
long when) {
@@ -1185,7 +1185,7 @@ public final class MessageQueue {
return findOrRemoveMessages(h, what, object, null, 0, mMatchHandlerWhatAndObject, false);
}
- private static class MatchHandlerWhatAndObjectEquals extends MessageCompare {
+ private static final class MatchHandlerWhatAndObjectEquals extends MessageCompare {
@Override
public boolean compareMessage(Message m, Handler h, int what, Object object, Runnable r,
long when) {
@@ -1206,7 +1206,7 @@ public final class MessageQueue {
false);
}
- private static class MatchHandlerRunnableAndObject extends MessageCompare {
+ private static final class MatchHandlerRunnableAndObject extends MessageCompare {
@Override
public boolean compareMessage(Message m, Handler h, int what, Object object, Runnable r,
long when) {
@@ -1227,7 +1227,7 @@ public final class MessageQueue {
return findOrRemoveMessages(h, -1, object, r, 0, mMatchHandlerRunnableAndObject, false);
}
- private static class MatchHandler extends MessageCompare {
+ private static final class MatchHandler extends MessageCompare {
@Override
public boolean compareMessage(Message m, Handler h, int what, Object object, Runnable r,
long when) {
@@ -1266,7 +1266,7 @@ public final class MessageQueue {
findOrRemoveMessages(h, -1, object, r, 0, mMatchHandlerRunnableAndObject, true);
}
- private static class MatchHandlerRunnableAndObjectEquals extends MessageCompare {
+ private static final class MatchHandlerRunnableAndObjectEquals extends MessageCompare {
@Override
public boolean compareMessage(Message m, Handler h, int what, Object object, Runnable r,
long when) {
@@ -1285,7 +1285,7 @@ public final class MessageQueue {
findOrRemoveMessages(h, -1, object, r, 0, mMatchHandlerRunnableAndObjectEquals, true);
}
- private static class MatchHandlerAndObject extends MessageCompare {
+ private static final class MatchHandlerAndObject extends MessageCompare {
@Override
public boolean compareMessage(Message m, Handler h, int what, Object object, Runnable r,
long when) {
@@ -1303,7 +1303,7 @@ public final class MessageQueue {
findOrRemoveMessages(h, -1, object, null, 0, mMatchHandlerAndObject, true);
}
- private static class MatchHandlerAndObjectEquals extends MessageCompare {
+ private static final class MatchHandlerAndObjectEquals extends MessageCompare {
@Override
public boolean compareMessage(Message m, Handler h, int what, Object object, Runnable r,
long when) {
@@ -1322,7 +1322,7 @@ public final class MessageQueue {
findOrRemoveMessages(h, -1, object, null, 0, mMatchHandlerAndObjectEquals, true);
}
- private static class MatchAllMessages extends MessageCompare {
+ private static final class MatchAllMessages extends MessageCompare {
@Override
public boolean compareMessage(Message m, Handler h, int what, Object object, Runnable r,
long when) {
@@ -1334,7 +1334,7 @@ public final class MessageQueue {
findOrRemoveMessages(null, -1, null, null, 0, mMatchAllMessages, true);
}
- private static class MatchAllFutureMessages extends MessageCompare {
+ private static final class MatchAllFutureMessages extends MessageCompare {
@Override
public boolean compareMessage(Message m, Handler h, int what, Object object, Runnable r,
long when) {
diff --git a/core/java/android/os/StrictMode.java b/core/java/android/os/StrictMode.java
index 81dc46ecbd3a..b5ecc1430cf7 100644
--- a/core/java/android/os/StrictMode.java
+++ b/core/java/android/os/StrictMode.java
@@ -20,17 +20,22 @@ import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
import static com.android.internal.util.FrameworkStatsLog.UNSAFE_INTENT_EVENT_REPORTED__EVENT_TYPE__EXPLICIT_INTENT_FILTER_UNMATCH;
import static com.android.internal.util.FrameworkStatsLog.UNSAFE_INTENT_EVENT_REPORTED__EVENT_TYPE__INTERNAL_NON_EXPORTED_COMPONENT_MATCH;
import static com.android.internal.util.FrameworkStatsLog.UNSAFE_INTENT_EVENT_REPORTED__EVENT_TYPE__NULL_ACTION_MATCH;
+import static com.android.window.flags.Flags.balStrictModeRo;
import android.animation.ValueAnimator;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.app.ActivityManager;
+import android.app.ActivityTaskManager;
import android.app.ActivityThread;
import android.app.IActivityManager;
+import android.app.IBackgroundActivityLaunchCallback;
import android.app.IUnsafeIntentStrictModeCallback;
+import android.app.PendingIntent;
import android.app.compat.CompatChanges;
import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledSince;
@@ -45,6 +50,7 @@ import android.content.res.Configuration;
import android.net.TrafficStats;
import android.net.Uri;
import android.os.storage.IStorageManager;
+import android.os.strictmode.BackgroundActivityLaunchViolation;
import android.os.strictmode.CleartextNetworkViolation;
import android.os.strictmode.ContentUriWithoutPermissionViolation;
import android.os.strictmode.CredentialProtectedWhileLockedViolation;
@@ -82,6 +88,7 @@ import com.android.internal.os.RuntimeInit;
import com.android.internal.util.FastPrintWriter;
import com.android.internal.util.HexDump;
import com.android.internal.util.Preconditions;
+import com.android.window.flags.Flags;
import dalvik.system.BlockGuard;
import dalvik.system.CloseGuard;
@@ -266,6 +273,7 @@ public final class StrictMode {
DETECT_VM_IMPLICIT_DIRECT_BOOT,
DETECT_VM_INCORRECT_CONTEXT_USE,
DETECT_VM_UNSAFE_INTENT_LAUNCH,
+ DETECT_VM_BACKGROUND_ACTIVITY_LAUNCH_ABORTED,
PENALTY_GATHER,
PENALTY_LOG,
PENALTY_DIALOG,
@@ -309,6 +317,8 @@ public final class StrictMode {
private static final int DETECT_VM_INCORRECT_CONTEXT_USE = 1 << 12;
/** @hide */
private static final int DETECT_VM_UNSAFE_INTENT_LAUNCH = 1 << 13;
+ /** @hide */
+ private static final int DETECT_VM_BACKGROUND_ACTIVITY_LAUNCH_ABORTED = 1 << 14;
/** @hide */
private static final int DETECT_VM_ALL = 0x0000ffff;
@@ -902,6 +912,9 @@ public final class StrictMode {
if (targetSdk >= Build.VERSION_CODES.S) {
detectUnsafeIntentLaunch();
}
+ if (balStrictModeRo() && targetSdk > Build.VERSION_CODES.VANILLA_ICE_CREAM) {
+ detectBlockedBackgroundActivityLaunch();
+ }
// TODO: Decide whether to detect non SDK API usage beyond a certain API level.
// TODO: enable detectImplicitDirectBoot() once system is less noisy
@@ -1140,6 +1153,39 @@ public final class StrictMode {
}
/**
+ * Detects when your app is blocked from launching a background activity or a
+ * PendingIntent created by your app cannot be launched.
+ * <p>
+ * Starting an activity requires <a
+ * href="https://developer.android.com/guide/components/activities/background-starts
+ * ">specific permissions</a> which may depend on the state at runtime and especially
+ * in case of {@link android.app.PendingIntent} starts on the collaborating app.
+ * If the activity start is blocked methods like {@link Context#startActivity(Intent)}
+ * or {@link PendingIntent#send()} have no way to return that information. Instead you
+ * can use this strct mode feature to detect blocked starts.
+ * <p>
+ * Note that in some cases blocked starts may be unavoidable, e.g. when the user clicks
+ * the home button while the app tries to start a new activity.
+ */
+ @SuppressWarnings("BuilderSetStyle")
+ @FlaggedApi(Flags.FLAG_BAL_STRICT_MODE_RO)
+ public @NonNull Builder detectBlockedBackgroundActivityLaunch() {
+ return enable(DETECT_VM_BACKGROUND_ACTIVITY_LAUNCH_ABORTED);
+ }
+
+ /**
+ * Stops detecting whether your app is blocked from launching a background activity or
+ * a PendingIntent created by your app cannot be launched.
+ * <p>
+ * This disables the effect of {@link #detectBlockedBackgroundActivityLaunch()}.
+ */
+ @SuppressWarnings("BuilderSetStyle")
+ @FlaggedApi(Flags.FLAG_BAL_STRICT_MODE_RO)
+ public @NonNull Builder ignoreBlockedBackgroundActivityLaunch() {
+ return disable(DETECT_VM_BACKGROUND_ACTIVITY_LAUNCH_ABORTED);
+ }
+
+ /**
* Crashes the whole process on violation. This penalty runs at the end of all enabled
* penalties so you'll still get your logging or other violations before the process
* dies.
@@ -2133,10 +2179,25 @@ public final class StrictMode {
registerIntentMatchingRestrictionCallback();
}
+ if ((sVmPolicy.mask & DETECT_VM_BACKGROUND_ACTIVITY_LAUNCH_ABORTED) != 0) {
+ registerBackgroundActivityLaunchCallback();
+ }
+
setBlockGuardVmPolicy(sVmPolicy.mask);
}
}
+ private static void registerBackgroundActivityLaunchCallback() {
+ try {
+ ActivityTaskManager.getService().registerBackgroundActivityStartCallback(
+ new BackgroundActivityLaunchCallback());
+ } catch (DeadObjectException e) {
+ // ignore
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException handling StrictMode violation", e);
+ }
+ }
+
private static final class UnsafeIntentStrictModeCallback
extends IUnsafeIntentStrictModeCallback.Stub {
@Override
@@ -2161,6 +2222,16 @@ public final class StrictMode {
}
}
+ private static final class BackgroundActivityLaunchCallback
+ extends IBackgroundActivityLaunchCallback.Stub {
+ @Override
+ public void onBackgroundActivityLaunchAborted(String message) {
+ if (StrictMode.vmBackgroundActivityLaunchEnabled()) {
+ StrictMode.onBackgroundActivityLaunchAborted(message);
+ }
+ }
+ }
+
/** Gets the current VM policy. */
public static VmPolicy getVmPolicy() {
synchronized (StrictMode.class) {
@@ -2236,6 +2307,11 @@ public final class StrictMode {
}
/** @hide */
+ public static boolean vmBackgroundActivityLaunchEnabled() {
+ return (sVmPolicy.mask & DETECT_VM_BACKGROUND_ACTIVITY_LAUNCH_ABORTED) != 0;
+ }
+
+ /** @hide */
public static void onSqliteObjectLeaked(String message, Throwable originStack) {
onVmPolicyViolation(new SqliteObjectLeakedViolation(message, originStack));
}
@@ -2402,6 +2478,11 @@ public final class StrictMode {
onVmPolicyViolation(new UnsafeIntentLaunchViolation(intent, msg + intent));
}
+ /** @hide */
+ public static void onBackgroundActivityLaunchAborted(String message) {
+ onVmPolicyViolation(new BackgroundActivityLaunchViolation(message));
+ }
+
/** Assume locked until we hear otherwise */
private static volatile boolean sCeStorageUnlocked = false;
diff --git a/core/java/android/os/strictmode/BackgroundActivityLaunchViolation.java b/core/java/android/os/strictmode/BackgroundActivityLaunchViolation.java
new file mode 100644
index 000000000000..aef52c6f275f
--- /dev/null
+++ b/core/java/android/os/strictmode/BackgroundActivityLaunchViolation.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.os.strictmode;
+
+import android.annotation.NonNull;
+import android.app.Activity;
+
+/**
+ * Violation raised when your app is blocked from launching an {@link Activity}
+ * (from the background).
+ * <p>
+ * This occurs when the app:
+ * <ul>
+ * <li>Does not have sufficient privileges to launch the Activity.</li>
+ * <li>Has not explicitly opted-in to launch the Activity.</li>
+ * </ul>
+ * Violations may affect the functionality of your app and should be addressed to ensure
+ * proper behavior.
+ * @hide
+ */
+public class BackgroundActivityLaunchViolation extends Violation {
+
+ /** @hide */
+ public BackgroundActivityLaunchViolation(@NonNull String message) {
+ super(message);
+ }
+}
diff --git a/core/java/android/permission/flags.aconfig b/core/java/android/permission/flags.aconfig
index 7944be140960..6a4932211f27 100644
--- a/core/java/android/permission/flags.aconfig
+++ b/core/java/android/permission/flags.aconfig
@@ -314,3 +314,21 @@ flag {
description: "Enable AppOp mode caching in AppOpsManager"
bug: "366013082"
}
+
+flag {
+ name: "permission_tree_apis_deprecated"
+ is_fixed_read_only: true
+ is_exported: true
+ namespace: "permissions"
+ description: "This flag is used to deprecate permission tree related APIs"
+ bug: "376535612"
+}
+
+flag {
+ name: "enable_otp_in_text_classifiers"
+ is_fixed_read_only: true
+ is_exported: true
+ namespace: "permissions"
+ description: "Enables ExtServices to leverage TextClassifier for OTP detection"
+ bug: "351976749"
+}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 8efbc9c92675..d19681c86320 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -6332,6 +6332,27 @@ public final class Settings {
public static final String SCREEN_FLASH_NOTIFICATION_COLOR =
"screen_flash_notification_color_global";
+
+ /**
+ * A semi-colon separated list of Bluetooth hearing devices' local ambient volume.
+ * Each entry is encoded as a key=value list, separated by commas. Ex:
+ *
+ * "addr=XX:XX:XX:00:11,ambient=20,group_ambient=30;addr=XX:XX:XX:00:22,ambient=50"
+ *
+ * The following keys are supported:
+ * <pre>
+ * addr (String)
+ * ambient (int)
+ * group_ambient (int)
+ * control_expanded (boolean)
+ * </pre>
+ *
+ * Each entry must contains "addr" attribute, otherwise it'll be ignored.
+ * @hide
+ */
+ public static final String HEARING_DEVICE_LOCAL_AMBIENT_VOLUME =
+ "hearing_device_local_ambient_volume";
+
/**
* IMPORTANT: If you add a new public settings you also have to add it to
* PUBLIC_SETTINGS below. If the new setting is hidden you have to add
@@ -6476,6 +6497,7 @@ public final class Settings {
PRIVATE_SETTINGS.add(DEFAULT_DEVICE_FONT_SCALE);
PRIVATE_SETTINGS.add(MOUSE_REVERSE_VERTICAL_SCROLLING);
PRIVATE_SETTINGS.add(MOUSE_SWAP_PRIMARY_BUTTON);
+ PRIVATE_SETTINGS.add(HEARING_DEVICE_LOCAL_AMBIENT_VOLUME);
}
/**
diff --git a/core/java/android/security/advancedprotection/AdvancedProtectionFeature.aidl b/core/java/android/security/advancedprotection/AdvancedProtectionFeature.aidl
new file mode 100644
index 000000000000..3ecef0248e1f
--- /dev/null
+++ b/core/java/android/security/advancedprotection/AdvancedProtectionFeature.aidl
@@ -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 android.security.advancedprotection;
+
+/**
+ * Represents an advanced protection feature providing protections
+ * @hide
+ */
+parcelable AdvancedProtectionFeature; \ No newline at end of file
diff --git a/core/java/android/security/advancedprotection/AdvancedProtectionFeature.java b/core/java/android/security/advancedprotection/AdvancedProtectionFeature.java
new file mode 100644
index 000000000000..a086bf7f8b08
--- /dev/null
+++ b/core/java/android/security/advancedprotection/AdvancedProtectionFeature.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.security.advancedprotection;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.security.Flags;
+
+/**
+ * An advanced protection feature providing protections.
+ * @hide
+ */
+@FlaggedApi(Flags.FLAG_AAPM_API)
+@SystemApi
+public final class AdvancedProtectionFeature implements Parcelable {
+ private final String mId;
+
+ /**
+ * Create an object identifying an Advanced Protection feature for AdvancedProtectionManager
+ * @param id A unique ID to identify this feature. It is used by Settings screens to display
+ * information about this feature.
+ */
+ public AdvancedProtectionFeature(@NonNull String id) {
+ mId = id;
+ }
+
+ private AdvancedProtectionFeature(Parcel in) {
+ mId = in.readString8();
+ }
+
+ /**
+ * @return the unique ID representing this feature
+ */
+ @NonNull
+ public String getId() {
+ return mId;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeString8(mId);
+ }
+
+ @NonNull
+ public static final Parcelable.Creator<AdvancedProtectionFeature> CREATOR =
+ new Parcelable.Creator<>() {
+ public AdvancedProtectionFeature createFromParcel(Parcel in) {
+ return new AdvancedProtectionFeature(in);
+ }
+
+ public AdvancedProtectionFeature[] newArray(int size) {
+ return new AdvancedProtectionFeature[size];
+ }
+ };
+}
diff --git a/core/java/android/security/advancedprotection/AdvancedProtectionManager.java b/core/java/android/security/advancedprotection/AdvancedProtectionManager.java
index 43b6ebe2a735..6f3e3d8f0d3b 100644
--- a/core/java/android/security/advancedprotection/AdvancedProtectionManager.java
+++ b/core/java/android/security/advancedprotection/AdvancedProtectionManager.java
@@ -29,6 +29,7 @@ import android.os.RemoteException;
import android.security.Flags;
import android.util.Log;
+import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executor;
@@ -41,7 +42,7 @@ import java.util.concurrent.Executor;
*/
@FlaggedApi(Flags.FLAG_AAPM_API)
@SystemService(Context.ADVANCED_PROTECTION_SERVICE)
-public class AdvancedProtectionManager {
+public final class AdvancedProtectionManager {
private static final String TAG = "AdvancedProtectionMgr";
private final ConcurrentHashMap<Callback, IAdvancedProtectionCallback>
@@ -147,6 +148,22 @@ public class AdvancedProtectionManager {
}
/**
+ * Returns the list of advanced protection features which are available on this device.
+ *
+ * @hide
+ */
+ @SystemApi
+ @NonNull
+ @RequiresPermission(Manifest.permission.SET_ADVANCED_PROTECTION_MODE)
+ public List<AdvancedProtectionFeature> getAdvancedProtectionFeatures() {
+ try {
+ return mService.getAdvancedProtectionFeatures();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* A callback class for monitoring changes to Advanced Protection state
*
* <p>To register a callback, implement this interface, and register it with
diff --git a/core/java/android/security/advancedprotection/IAdvancedProtectionService.aidl b/core/java/android/security/advancedprotection/IAdvancedProtectionService.aidl
index ef0abf4f23a0..68307632027a 100644
--- a/core/java/android/security/advancedprotection/IAdvancedProtectionService.aidl
+++ b/core/java/android/security/advancedprotection/IAdvancedProtectionService.aidl
@@ -16,6 +16,7 @@
package android.security.advancedprotection;
+import android.security.advancedprotection.AdvancedProtectionFeature;
import android.security.advancedprotection.IAdvancedProtectionCallback;
/**
@@ -32,4 +33,6 @@ interface IAdvancedProtectionService {
void unregisterAdvancedProtectionCallback(IAdvancedProtectionCallback callback);
@EnforcePermission("SET_ADVANCED_PROTECTION_MODE")
void setAdvancedProtectionEnabled(boolean enabled);
+ @EnforcePermission("SET_ADVANCED_PROTECTION_MODE")
+ List<AdvancedProtectionFeature> getAdvancedProtectionFeatures();
} \ No newline at end of file
diff --git a/core/java/android/security/responsible_apis_flags.aconfig b/core/java/android/security/responsible_apis_flags.aconfig
index dec28c34ae5b..5995760a41ec 100644
--- a/core/java/android/security/responsible_apis_flags.aconfig
+++ b/core/java/android/security/responsible_apis_flags.aconfig
@@ -87,6 +87,14 @@ flag {
}
flag {
+ name: "prevent_intent_redirect_show_toast"
+ namespace: "responsible_apis"
+ description: "Prevent intent redirect attacks by showing a toast when activity start is blocked"
+ bug: "361143368"
+ is_fixed_read_only: true
+}
+
+flag {
name: "enable_intent_matching_flags"
is_exported: true
namespace: "permissions"
diff --git a/core/java/android/service/contextualsearch/OWNERS b/core/java/android/service/contextualsearch/OWNERS
index b7238721bc60..c435bd87be21 100644
--- a/core/java/android/service/contextualsearch/OWNERS
+++ b/core/java/android/service/contextualsearch/OWNERS
@@ -1,2 +1,3 @@
srazdan@google.com
-hackz@google.com
+hyunyoungs@google.com
+awickham@google.com
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index bd9ab86fa8d1..a8ab2115d97f 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -17,6 +17,7 @@
package android.service.notification;
import android.annotation.CurrentTimeMillisLong;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -882,6 +883,35 @@ public abstract class NotificationListenerService extends Service {
}
}
+ /**
+ * Creates a conversation notification channel for a given package for a given user.
+ *
+ * <p>This method will throw a security exception if you don't have access to notifications
+ * for the given user.</p>
+ * <p>The caller must have {@link CompanionDeviceManager#getAssociations() an associated
+ * device} or be the notification assistant in order to use this method.
+ *
+ * @param pkg The package the channel belongs to.
+ * @param user The user the channel belongs to.
+ * @param parentChannelId The parent channel id of the conversation channel belongs to.
+ * @param conversationId The conversation id of the conversation channel.
+ *
+ * @return The created conversation channel.
+ */
+ @FlaggedApi(Flags.FLAG_NOTIFICATION_CONVERSATION_CHANNEL_MANAGEMENT)
+ public final @Nullable NotificationChannel createConversationNotificationChannelForPackage(
+ @NonNull String pkg, @NonNull UserHandle user, @NonNull String parentChannelId,
+ @NonNull String conversationId) {
+ if (!isBound()) return null;
+ try {
+ return getNotificationInterface()
+ .createConversationNotificationChannelForPackageFromPrivilegedListener(
+ mWrapper, pkg, user, parentChannelId, conversationId);
+ } catch (RemoteException e) {
+ Log.v(TAG, "Unable to contact notification manager", e);
+ throw e.rethrowFromSystemServer();
+ }
+ }
/**
* Updates a notification channel for a given package for a given user. This should only be used
@@ -890,7 +920,7 @@ public abstract class NotificationListenerService extends Service {
* <p>This method will throw a security exception if you don't have access to notifications
* for the given user.</p>
* <p>The caller must have {@link CompanionDeviceManager#getAssociations() an associated
- * device} in order to use this method.
+ * device} or be the notification assistant in order to use this method.
*
* @param pkg The package the channel belongs to.
* @param user The user the channel belongs to.
diff --git a/core/java/android/service/notification/flags.aconfig b/core/java/android/service/notification/flags.aconfig
index 51961a85d307..34e311f8c932 100644
--- a/core/java/android/service/notification/flags.aconfig
+++ b/core/java/android/service/notification/flags.aconfig
@@ -57,4 +57,12 @@ flag {
namespace: "systemui"
description: "Guards the new FLAG_SILENT Notification flag"
bug: "336488844"
+}
+
+flag {
+ name: "notification_conversation_channel_management"
+ is_exported: true
+ namespace: "systemui"
+ description: "Allows the NAS to create and modify conversation notifications"
+ bug: "373599715"
} \ No newline at end of file
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index 2ab16e91d987..7f54cf0999b0 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -221,14 +221,14 @@ public abstract class WallpaperService extends Service {
/**
* Wear products currently force a slight scaling transition to wallpapers
- * when the QSS is opened. However, on Wear 6 (SDK 35) and above, 1P watch faces
+ * when the QSS is opened. However, on Wear 7 (SDK 37) and above, 1P watch faces
* will be expected to either implement their own scaling, or to override this
* method to allow the WallpaperController to continue to scale for them.
*
* @hide
*/
@ChangeId
- @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM)
+ @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.BAKLAVA)
public static final long WEAROS_WALLPAPER_HANDLES_SCALING = 272527315L;
static final class WallpaperCommand {
diff --git a/core/java/android/text/flags/flags.aconfig b/core/java/android/text/flags/flags.aconfig
index 09b2201efb4e..e830d89d3116 100644
--- a/core/java/android/text/flags/flags.aconfig
+++ b/core/java/android/text/flags/flags.aconfig
@@ -195,3 +195,10 @@ flag {
description: "Feature flag for adding a TYPE_DURATION to TtsSpan"
bug: "337103893"
}
+
+flag {
+ name: "deprecate_elegant_text_height_api"
+ namespace: "text"
+ description: "Deprecate the Paint#elegantTextHeight API and stick it to true"
+ bug: "349519475"
+}
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 0d5554484dc1..b8b22e283175 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -87,6 +87,7 @@ import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.List;
import java.util.Objects;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
@@ -463,7 +464,7 @@ public final class SurfaceControl implements Parcelable {
/**
* Called when new jank classifications are available.
*/
- void onJankDataAvailable(JankData[] jankData);
+ void onJankDataAvailable(@NonNull List<JankData> jankData);
}
@@ -2686,7 +2687,9 @@ public final class SurfaceControl implements Parcelable {
* Adds a callback to be informed about SF's jank classification for this surface.
* @hide
*/
- public OnJankDataListenerRegistration addJankDataListener(OnJankDataListener listener) {
+ @NonNull
+ public OnJankDataListenerRegistration addOnJankDataListener(
+ @NonNull OnJankDataListener listener) {
return new OnJankDataListenerRegistration(this, listener);
}
@@ -3459,15 +3462,15 @@ public final class SurfaceControl implements Parcelable {
* @return this This transaction for chaining
* @hide
*/
- public @NonNull Transaction setCrop(@NonNull SurfaceControl sc, float top, float left,
- float bottom, float right) {
+ public @NonNull Transaction setCrop(@NonNull SurfaceControl sc, float left, float top,
+ float right, float bottom) {
checkPreconditions(sc);
if (SurfaceControlRegistry.sCallStackDebuggingEnabled) {
SurfaceControlRegistry.getProcessInstance().checkCallStackDebugging(
"setCrop", this, sc, "crop={" + top + ", " + left + ", " +
bottom + ", " + right + "}");
}
- nativeSetCrop(mNativeObject, sc.mNativeObject, top, left, bottom, right);
+ nativeSetCrop(mNativeObject, sc.mNativeObject, left, top, right, bottom);
return this;
}
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 9cad3e58fa02..4df7649e1c9b 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -40,6 +40,7 @@ import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.Rect;
+import android.graphics.RectF;
import android.graphics.Region;
import android.graphics.RenderNode;
import android.hardware.input.InputManager;
@@ -1666,7 +1667,7 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
}
private final Rect mRTLastReportedPosition = new Rect();
- private final Rect mRTLastSetCrop = new Rect();
+ private final RectF mRTLastSetCrop = new RectF();
private class SurfaceViewPositionUpdateListener implements RenderNode.PositionUpdateListener {
private final int mRtSurfaceWidth;
@@ -1711,16 +1712,18 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
@Override
public void positionChanged(long frameNumber, int left, int top, int right, int bottom,
- int clipLeft, int clipTop, int clipRight, int clipBottom) {
+ int clipLeft, int clipTop, int clipRight, int clipBottom,
+ int nodeWidth, int nodeHeight) {
try {
if (DEBUG_POSITION) {
Log.d(TAG, String.format(
"%d updateSurfacePosition RenderWorker, frameNr = %d, "
+ "position = [%d, %d, %d, %d] clip = [%d, %d, %d, %d] "
- + "surfaceSize = %dx%d",
+ + "surfaceSize = %dx%d renderNodeSize = %d%d",
System.identityHashCode(SurfaceView.this), frameNumber,
left, top, right, bottom, clipLeft, clipTop, clipRight, clipBottom,
- mRtSurfaceWidth, mRtSurfaceHeight));
+ mRtSurfaceWidth, mRtSurfaceHeight,
+ nodeWidth, nodeHeight));
}
synchronized (mSurfaceControlLock) {
if (mSurfaceControl == null) return;
@@ -1735,14 +1738,29 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
mRTLastReportedPosition.top /*positionTop*/,
postScaleX, postScaleY);
- mRTLastSetCrop.set(clipLeft, clipTop, clipRight, clipBottom);
+ // The computed crop is in view-relative dimensions, however we need it to be
+ // in buffer-relative dimensions. So scale the crop by the ratio between
+ // the view's unscaled width/height (nodeWidth/Height), and the surface's
+ // width/height
+ // That is, if the Surface has a fixed size of 50x50, the SurfaceView is laid
+ // out to a size of 100x100, and the SurfaceView is ultimately scaled to
+ // 1000x1000, then we need to scale the crop by just the 2x from surface
+ // domain to SV domain.
+ final float surfaceToNodeScaleX = (float) mRtSurfaceWidth / (float) nodeWidth;
+ final float surfaceToNodeScaleY = (float) mRtSurfaceHeight / (float) nodeHeight;
+ mRTLastSetCrop.set(clipLeft * surfaceToNodeScaleX,
+ clipTop * surfaceToNodeScaleY,
+ clipRight * surfaceToNodeScaleX,
+ clipBottom * surfaceToNodeScaleY);
+
if (DEBUG_POSITION) {
- Log.d(TAG, String.format("Setting layer crop = [%d, %d, %d, %d] "
+ Log.d(TAG, String.format("Setting layer crop = [%f, %f, %f, %f] "
+ "from scale %f, %f", mRTLastSetCrop.left,
mRTLastSetCrop.top, mRTLastSetCrop.right, mRTLastSetCrop.bottom,
- postScaleX, postScaleY));
+ surfaceToNodeScaleX, surfaceToNodeScaleY));
}
- mPositionChangedTransaction.setCrop(mSurfaceControl, mRTLastSetCrop);
+ mPositionChangedTransaction.setCrop(mSurfaceControl, mRTLastSetCrop.left,
+ mRTLastSetCrop.top, mRTLastSetCrop.right, mRTLastSetCrop.bottom);
if (mRTLastSetCrop.isEmpty()) {
mPositionChangedTransaction.hide(mSurfaceControl);
} else {
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index d5fc26281fc6..6090b5ec1622 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -30,7 +30,9 @@ import static android.view.Surface.FRAME_RATE_COMPATIBILITY_FIXED_SOURCE;
import static android.view.Surface.FRAME_RATE_COMPATIBILITY_GTE;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
import static android.view.accessibility.AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED;
+import static android.view.accessibility.Flags.FLAG_SUPPLEMENTAL_DESCRIPTION;
import static android.view.accessibility.Flags.removeChildHoverCheckForTouchExploration;
+import static android.view.accessibility.Flags.supplementalDescription;
import static android.view.displayhash.DisplayHashResultCallback.DISPLAY_HASH_ERROR_INVALID_BOUNDS;
import static android.view.displayhash.DisplayHashResultCallback.DISPLAY_HASH_ERROR_MISSING_WINDOW;
import static android.view.displayhash.DisplayHashResultCallback.DISPLAY_HASH_ERROR_NOT_VISIBLE_ON_SCREEN;
@@ -40,7 +42,6 @@ import static android.view.displayhash.DisplayHashResultCallback.EXTRA_DISPLAY_H
import static android.view.flags.Flags.FLAG_SENSITIVE_CONTENT_APP_PROTECTION_API;
import static android.view.flags.Flags.FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY;
import static android.view.flags.Flags.FLAG_VIEW_VELOCITY_API;
-import static android.view.flags.Flags.calculateBoundsInParentFromBoundsInScreen;
import static android.view.flags.Flags.enableUseMeasureCacheDuringForceLayout;
import static android.view.flags.Flags.sensitiveContentAppProtection;
import static android.view.flags.Flags.toolkitFrameRateAnimationBugfix25q1;
@@ -968,13 +969,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
private static boolean sAlwaysRemeasureExactly = false;
/**
- * When true calculates the bounds in parent from bounds in screen relative to its parents.
- * This addresses the deprecated API (setBoundsInParent) in Compose, which causes empty
- * getBoundsInParent call for Compose apps.
- */
- private static boolean sCalculateBoundsInParentFromBoundsInScreenFlagValue = false;
-
- /**
* When true makes it possible to use onMeasure caches also when the force layout flag is
* enabled. This helps avoiding multiple measures in the same frame with the same dimensions.
*/
@@ -2564,8 +2558,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
sToolkitSetFrameRateReadOnlyFlagValue = toolkitSetFrameRateReadOnly();
sToolkitMetricsForFrameRateDecisionFlagValue = toolkitMetricsForFrameRateDecision();
- sCalculateBoundsInParentFromBoundsInScreenFlagValue =
- calculateBoundsInParentFromBoundsInScreen();
sUseMeasureCacheDuringForceLayoutFlagValue = enableUseMeasureCacheDuringForceLayout();
}
@@ -4874,6 +4866,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
private CharSequence mContentDescription;
/**
+ * Brief supplemental information for view and is primarily used for accessibility support.
+ */
+ private CharSequence mSupplementalDescription;
+
+ /**
* If this view represents a distinct part of the window, it can have a title that labels the
* area.
*/
@@ -6544,6 +6541,13 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
case R.styleable.View_contentSensitivity:
setContentSensitivity(a.getInt(attr, CONTENT_SENSITIVITY_AUTO));
break;
+ default: {
+ if (supplementalDescription()) {
+ if (attr == com.android.internal.R.styleable.View_supplementalDescription) {
+ setSupplementalDescription(a.getString(attr));
+ }
+ }
+ }
}
}
@@ -9786,7 +9790,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
structure.setChildCount(1);
final ViewStructure root = structure.newChild(0);
if (info != null) {
- populateVirtualStructure(root, provider, info, null, forAutofill);
+ populateVirtualStructure(root, provider, info, forAutofill);
info.recycle();
} else {
Log.w(AUTOFILL_LOG_TAG, "AccessibilityNodeInfo is null.");
@@ -11085,19 +11089,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
private void populateVirtualStructure(ViewStructure structure,
AccessibilityNodeProvider provider, AccessibilityNodeInfo info,
- @Nullable AccessibilityNodeInfo parentInfo, boolean forAutofill) {
+ boolean forAutofill) {
structure.setId(AccessibilityNodeInfo.getVirtualDescendantId(info.getSourceNodeId()),
null, null, info.getViewIdResourceName());
Rect rect = structure.getTempRect();
- // The bounds in parent for Jetpack Compose views aren't set as setBoundsInParent is
- // deprecated, and only setBoundsInScreen is called.
- // The bounds in parent can be calculated by diff'ing the child view's bounds in screen with
- // the parent's.
- if (sCalculateBoundsInParentFromBoundsInScreenFlagValue) {
- getBoundsInParent(info, parentInfo, rect);
- } else {
- info.getBoundsInParent(rect);
- }
+ info.getBoundsInParent(rect);
structure.setDimens(rect.left, rect.top, 0, 0, rect.width(), rect.height());
structure.setVisibility(VISIBLE);
structure.setEnabled(info.isEnabled());
@@ -11181,32 +11177,13 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
AccessibilityNodeInfo.getVirtualDescendantId(info.getChildId(i)));
if (cinfo != null) {
ViewStructure child = structure.newChild(i);
- populateVirtualStructure(child, provider, cinfo, info, forAutofill);
+ populateVirtualStructure(child, provider, cinfo, forAutofill);
cinfo.recycle();
}
}
}
}
- private void getBoundsInParent(@NonNull AccessibilityNodeInfo info,
- @Nullable AccessibilityNodeInfo parentInfo, @NonNull Rect rect) {
- info.getBoundsInParent(rect);
- // Fallback to calculate bounds in parent by diffing the bounds in
- // screen if it's all 0.
- if ((rect.left | rect.top | rect.right | rect.bottom) == 0) {
- if (parentInfo != null) {
- Rect parentBoundsInScreen = parentInfo.getBoundsInScreen();
- Rect boundsInScreen = info.getBoundsInScreen();
- rect.set(boundsInScreen.left - parentBoundsInScreen.left,
- boundsInScreen.top - parentBoundsInScreen.top,
- boundsInScreen.right - parentBoundsInScreen.left,
- boundsInScreen.bottom - parentBoundsInScreen.top);
- } else {
- info.getBoundsInScreen(rect);
- }
- }
- }
-
/**
* Dispatch creation of {@link ViewStructure} down the hierarchy. The default
* implementation calls {@link #onProvideStructure} and
@@ -11843,6 +11820,39 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
}
/**
+ * Returns the {@link View}'s supplemental description.
+ * <p>
+ * A supplemental description provides
+ * brief supplemental information for this node, such as the purpose of the node when
+ * that purpose is not conveyed within its textual representation. For example, if a
+ * dropdown select has a purpose of setting font family, the supplemental description
+ * could be "font family". If this node has children, its supplemental description serves
+ * as additional information and is not intended to replace any existing information
+ * in the subtree. This is different from the {@link #getContentDescription()} in that
+ * this description is purely supplemental while a content description may be used
+ * to replace a description for a node or its subtree that an assistive technology
+ * would otherwise compute based on other properties of the node and its descendants.
+ *
+ * <p>
+ * <strong>Note:</strong> Do not override this method, as it will have no
+ * effect on the supplemental description presented to accessibility services.
+ * You must call {@link #setSupplementalDescription(CharSequence)} to modify the
+ * supplemental description.
+ *
+ * @return the supplemental description
+ * @see #setSupplementalDescription(CharSequence)
+ * @see #getContentDescription()
+ * @attr ref android.R.styleable#View_supplementalDescription
+ */
+ @FlaggedApi(FLAG_SUPPLEMENTAL_DESCRIPTION)
+ @ViewDebug.ExportedProperty(category = "accessibility")
+ @InspectableProperty
+ @Nullable
+ public CharSequence getSupplementalDescription() {
+ return mSupplementalDescription;
+ }
+
+ /**
* Sets the {@link View}'s state description.
* <p>
* A state description briefly describes the states of the view and is primarily used
@@ -11929,6 +11939,53 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
}
/**
+ * Sets the {@link View}'s supplemental description.
+ * <p>
+ * A supplemental description provides
+ * brief supplemental information for this node, such as the purpose of the node when
+ * that purpose is not conveyed within its textual representation. For example, if a
+ * dropdown select has a purpose of setting font family, the supplemental description
+ * could be "font family". If this node has children, its supplemental description serves
+ * as additional information and is not intended to replace any existing information
+ * in the subtree. This is different from the {@link #setContentDescription(CharSequence)}
+ * in that this description is purely supplemental while a content description may be used
+ * to replace a description for a node or its subtree that an assistive technology
+ * would otherwise compute based on other properties of the node and its descendants.
+ *
+ * <p>
+ * This should omit role or state. Role refers to the kind of user-interface element the View
+ * is, such as a Button or Checkbox. State refers to a frequently changing property of the View,
+ * such as an On/Off state of a button or the audio level of a volume slider.
+ *
+ * @param supplementalDescription The supplemental description.
+ * @see #getSupplementalDescription()
+ * @see #setContentDescription(CharSequence)
+ * @see #setStateDescription(CharSequence) for state changes.
+ * @attr ref android.R.styleable#View_supplementalDescription
+ */
+ @FlaggedApi(FLAG_SUPPLEMENTAL_DESCRIPTION)
+ @RemotableViewMethod
+ public void setSupplementalDescription(@Nullable CharSequence supplementalDescription) {
+ if (mSupplementalDescription == null) {
+ if (supplementalDescription == null) {
+ return;
+ }
+ } else if (mSupplementalDescription.equals(supplementalDescription)) {
+ return;
+ }
+ mSupplementalDescription = supplementalDescription;
+ final boolean nonEmptyDesc = supplementalDescription != null
+ && !supplementalDescription.isEmpty();
+ if (nonEmptyDesc && getImportantForAccessibility() == IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
+ setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
+ notifySubtreeAccessibilityStateChangedIfNeeded();
+ } else {
+ notifyViewAccessibilityStateChangedIfNeeded(
+ AccessibilityEvent.CONTENT_CHANGE_TYPE_SUPPLEMENTAL_DESCRIPTION);
+ }
+ }
+
+ /**
* Sets the id of a view that screen readers are requested to visit after this view.
*
* <p>
diff --git a/core/java/android/view/accessibility/AccessibilityEvent.java b/core/java/android/view/accessibility/AccessibilityEvent.java
index 4e9d054dae07..c690787e4a33 100644
--- a/core/java/android/view/accessibility/AccessibilityEvent.java
+++ b/core/java/android/view/accessibility/AccessibilityEvent.java
@@ -810,6 +810,20 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par
@FlaggedApi(Flags.FLAG_A11Y_EXPANSION_STATE_API)
public static final int CONTENT_CHANGE_TYPE_EXPANDED = 1 << 14;
+ /**
+ * Change type for {@link #TYPE_WINDOW_CONTENT_CHANGED} event:
+ * The source node changed its supplemental description, which is returned by
+ * {@link AccessibilityNodeInfo#getSupplementalDescription()}.
+ * The view changing its supplemental description should call
+ * {@link AccessibilityNodeInfo#setSupplementalDescription(CharSequence)} and
+ * then send this event.
+ *
+ * @see AccessibilityNodeInfo#getSupplementalDescription()
+ * @see AccessibilityNodeInfo#setSupplementalDescription(CharSequence)
+ */
+ @FlaggedApi(Flags.FLAG_SUPPLEMENTAL_DESCRIPTION)
+ public static final int CONTENT_CHANGE_TYPE_SUPPLEMENTAL_DESCRIPTION = 1 << 15;
+
// Speech state change types.
/** Change type for {@link #TYPE_SPEECH_STATE_CHANGE} event: another service is speaking. */
@@ -942,6 +956,7 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par
CONTENT_CHANGE_TYPE_CONTENT_INVALID,
CONTENT_CHANGE_TYPE_ERROR,
CONTENT_CHANGE_TYPE_ENABLED,
+ CONTENT_CHANGE_TYPE_SUPPLEMENTAL_DESCRIPTION,
})
public @interface ContentChangeTypes {}
@@ -1222,7 +1237,14 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par
return "CONTENT_CHANGE_TYPE_CONTENT_INVALID";
case CONTENT_CHANGE_TYPE_ERROR: return "CONTENT_CHANGE_TYPE_ERROR";
case CONTENT_CHANGE_TYPE_ENABLED: return "CONTENT_CHANGE_TYPE_ENABLED";
- default: return Integer.toHexString(type);
+ default: {
+ if (Flags.supplementalDescription()) {
+ if (type == CONTENT_CHANGE_TYPE_SUPPLEMENTAL_DESCRIPTION) {
+ return "CONTENT_CHANGE_TYPE_SUPPLEMENTAL_DESCRIPTION";
+ }
+ }
+ return Integer.toHexString(type);
+ }
}
}
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index f70f7e6a59c0..e7acfdf7b26e 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -1085,6 +1085,7 @@ public class AccessibilityNodeInfo implements Parcelable {
private CharSequence mPaneTitle;
private CharSequence mStateDescription;
private CharSequence mContentDescription;
+ private CharSequence mSupplementalDescription;
private CharSequence mTooltipText;
private String mViewIdResourceName;
private String mUniqueId;
@@ -3686,6 +3687,27 @@ public class AccessibilityNodeInfo implements Parcelable {
return mContentDescription;
}
+ /**
+ * Gets the supplemental description of this node. A supplemental description provides
+ * brief supplemental information for this node, such as the purpose of the node when
+ * that purpose is not conveyed within its textual representation. For example, if a
+ * dropdown select has a purpose of setting font family, the supplemental description
+ * could be "font family". If this node has children, its supplemental description serves
+ * as additional information and is not intended to replace any existing information
+ * in the subtree. This is different from the {@link #getContentDescription()} in that
+ * this description is purely supplemental while a content description may be used
+ * to replace a description for a node or its subtree that an assistive technology
+ * would otherwise compute based on other properties of the node and its descendants.
+ *
+ * @return The supplemental description.
+ * @see #setSupplementalDescription(CharSequence)
+ * @see #getContentDescription()
+ */
+ @FlaggedApi(Flags.FLAG_SUPPLEMENTAL_DESCRIPTION)
+ @Nullable
+ public CharSequence getSupplementalDescription() {
+ return mSupplementalDescription;
+ }
/**
* Sets the state description of this node.
@@ -3724,6 +3746,35 @@ public class AccessibilityNodeInfo implements Parcelable {
}
/**
+ * Sets the supplemental description of this node. A supplemental description provides
+ * brief supplemental information for this node, such as the purpose of the node when
+ * that purpose is not conveyed within its textual representation. For example, if a
+ * dropdown select has a purpose of setting font family, the supplemental description
+ * could be "font family". If this node has children, its supplemental description serves
+ * as additional information and is not intended to replace any existing information
+ * in the subtree. This is different from the {@link #setContentDescription(CharSequence)}
+ * in that this description is purely supplemental while a content description may be used
+ * to replace a description for a node or its subtree that an assistive technology
+ * would otherwise compute based on other properties of the node and its descendants.
+ * <p>
+ * <strong>Note:</strong> Cannot be called from an
+ * {@link android.accessibilityservice.AccessibilityService}.
+ * This class is made immutable before being delivered to an AccessibilityService.
+ *
+ * @param supplementalDescription The supplemental description.
+ *
+ * @throws IllegalStateException If called from an AccessibilityService.
+ * @see #getSupplementalDescription()
+ * @see #setContentDescription(CharSequence)
+ */
+ @FlaggedApi(Flags.FLAG_SUPPLEMENTAL_DESCRIPTION)
+ public void setSupplementalDescription(@Nullable CharSequence supplementalDescription) {
+ enforceNotSealed();
+ mSupplementalDescription = (supplementalDescription == null) ? null
+ : supplementalDescription.subSequence(0, supplementalDescription.length());
+ }
+
+ /**
* Gets the tooltip text of this node.
*
* @return The tooltip text.
@@ -4657,6 +4708,10 @@ public class AccessibilityNodeInfo implements Parcelable {
nonDefaultFields |= bitAt(fieldIndex);
}
fieldIndex++;
+ if (!Objects.equals(mSupplementalDescription, DEFAULT.mSupplementalDescription)) {
+ nonDefaultFields |= bitAt(fieldIndex);
+ }
+ fieldIndex++;
if (!Objects.equals(mPaneTitle, DEFAULT.mPaneTitle)) {
nonDefaultFields |= bitAt(fieldIndex);
}
@@ -4843,6 +4898,9 @@ public class AccessibilityNodeInfo implements Parcelable {
if (isBitSet(nonDefaultFields, fieldIndex++)) {
parcel.writeCharSequence(mContentDescription);
}
+ if (isBitSet(nonDefaultFields, fieldIndex++)) {
+ parcel.writeCharSequence(mSupplementalDescription);
+ }
if (isBitSet(nonDefaultFields, fieldIndex++)) parcel.writeCharSequence(mPaneTitle);
if (isBitSet(nonDefaultFields, fieldIndex++)) parcel.writeCharSequence(mTooltipText);
if (isBitSet(nonDefaultFields, fieldIndex++)) parcel.writeCharSequence(mContainerTitle);
@@ -4950,6 +5008,7 @@ public class AccessibilityNodeInfo implements Parcelable {
mError = other.mError;
mStateDescription = other.mStateDescription;
mContentDescription = other.mContentDescription;
+ mSupplementalDescription = other.mSupplementalDescription;
mPaneTitle = other.mPaneTitle;
mTooltipText = other.mTooltipText;
mContainerTitle = other.mContainerTitle;
@@ -5119,6 +5178,9 @@ public class AccessibilityNodeInfo implements Parcelable {
if (isBitSet(nonDefaultFields, fieldIndex++)) {
mContentDescription = parcel.readCharSequence();
}
+ if (isBitSet(nonDefaultFields, fieldIndex++)) {
+ mSupplementalDescription = parcel.readCharSequence();
+ }
if (isBitSet(nonDefaultFields, fieldIndex++)) mPaneTitle = parcel.readCharSequence();
if (isBitSet(nonDefaultFields, fieldIndex++)) mTooltipText = parcel.readCharSequence();
if (isBitSet(nonDefaultFields, fieldIndex++)) mContainerTitle = parcel.readCharSequence();
@@ -5483,6 +5545,9 @@ public class AccessibilityNodeInfo implements Parcelable {
builder.append("; maxTextLength: ").append(mMaxTextLength);
builder.append("; stateDescription: ").append(mStateDescription);
builder.append("; contentDescription: ").append(mContentDescription);
+ if (Flags.supplementalDescription()) {
+ builder.append("; supplementalDescription: ").append(mSupplementalDescription);
+ }
builder.append("; tooltipText: ").append(mTooltipText);
builder.append("; containerTitle: ").append(mContainerTitle);
builder.append("; viewIdResName: ").append(mViewIdResourceName);
diff --git a/core/java/android/view/accessibility/flags/accessibility_flags.aconfig b/core/java/android/view/accessibility/flags/accessibility_flags.aconfig
index ffa819f1f183..fde42af85420 100644
--- a/core/java/android/view/accessibility/flags/accessibility_flags.aconfig
+++ b/core/java/android/view/accessibility/flags/accessibility_flags.aconfig
@@ -222,6 +222,13 @@ flag {
}
flag {
+ name: "supplemental_description"
+ namespace: "accessibility"
+ description: "Feature flag for supplemental description api"
+ bug: "375266174"
+}
+
+flag {
name: "support_multiple_labeledby"
namespace: "accessibility"
description: "Feature flag for supporting multiple labels in AccessibilityNodeInfo labeledby api"
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index e6eec3760804..9b6311f35d17 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -20,7 +20,6 @@ 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.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;
@@ -55,10 +54,6 @@ 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;
-import android.content.om.OverlayManagerTransaction;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.ColorStateList;
@@ -84,12 +79,14 @@ import android.os.Bundle;
import android.os.CancellationSignal;
import android.os.IBinder;
import android.os.Parcel;
+import android.os.ParcelFileDescriptor;
import android.os.Parcelable;
import android.os.Process;
import android.os.RemoteException;
import android.os.StrictMode;
import android.os.Trace;
import android.os.UserHandle;
+import android.system.Os;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.DisplayMetrics;
@@ -131,8 +128,11 @@ import com.android.internal.widget.remotecompose.player.RemoteComposePlayer;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
+import java.io.FileDescriptor;
+import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.io.OutputStream;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -8552,8 +8552,12 @@ public class RemoteViews implements Parcelable, Filter {
* @hide
*/
public static final class ColorResources {
- private static final String OVERLAY_NAME = "remote_views_color_resources";
- private static final String OVERLAY_TARGET_PACKAGE_NAME = "android";
+ // Set of valid colors resources.
+ private static final int FIRST_RESOURCE_COLOR_ID = android.R.color.system_neutral1_0;
+ private static final int LAST_RESOURCE_COLOR_ID =
+ android.R.color.system_error_1000;
+ // Size, in bytes, of an entry in the array of colors in an ARSC file.
+ private static final int ARSC_ENTRY_SIZE = 16;
private final ResourcesLoader mLoader;
private final SparseIntArray mColorMapping;
@@ -8587,6 +8591,44 @@ public class RemoteViews implements Parcelable, Filter {
}
/**
+ * Creates the compiled resources content from the asset stored in the APK.
+ *
+ * The asset is a compiled resource with the correct resources name and correct ids, only
+ * the values are incorrect. The last value is at the very end of the file. The resources
+ * are in an array, the array's entries are 16 bytes each. We use this to work out the
+ * location of all the positions of the various resources.
+ */
+ @Nullable
+ private static byte[] createCompiledResourcesContent(Context context,
+ SparseIntArray colorResources) throws IOException {
+ byte[] content;
+ try (InputStream input = context.getResources().openRawResource(
+ com.android.internal.R.raw.remote_views_color_resources)) {
+ ByteArrayOutputStream rawContent = readFileContent(input);
+ content = rawContent.toByteArray();
+ }
+ int valuesOffset =
+ content.length - (LAST_RESOURCE_COLOR_ID & 0xffff) * ARSC_ENTRY_SIZE - 4;
+ if (valuesOffset < 0) {
+ Log.e(LOG_TAG, "ARSC file for theme colors is invalid.");
+ return null;
+ }
+ for (int colorRes = FIRST_RESOURCE_COLOR_ID; colorRes <= LAST_RESOURCE_COLOR_ID;
+ colorRes++) {
+ // The last 2 bytes are the index in the color array.
+ int index = colorRes & 0xffff;
+ int offset = valuesOffset + index * ARSC_ENTRY_SIZE;
+ int value = colorResources.get(colorRes, context.getColor(colorRes));
+ // Write the 32 bit integer in little endian
+ for (int b = 0; b < 4; b++) {
+ content[offset + b] = (byte) (value & 0xff);
+ value >>= 8;
+ }
+ }
+ return content;
+ }
+
+ /**
* Adds a resource loader for theme colors to the given context.
*
* @param context Context of the view hosting the widget.
@@ -8597,38 +8639,31 @@ public class RemoteViews implements Parcelable, Filter {
@Nullable
public static ColorResources create(Context context, SparseIntArray colorMapping) {
try {
- String owningPackage = context.getPackageName();
- FabricatedOverlay overlay = new FabricatedOverlay.Builder(owningPackage,
- OVERLAY_NAME, OVERLAY_TARGET_PACKAGE_NAME).build();
-
- for (int i = 0; i < colorMapping.size(); i++) {
- overlay.setResourceValue(
- context.getResources().getResourceName(colorMapping.keyAt(i)),
- TYPE_INT_COLOR_ARGB8, colorMapping.valueAt(i), null);
- }
- OverlayManager overlayManager = context.getSystemService(OverlayManager.class);
- OverlayManagerTransaction.Builder transaction =
- new OverlayManagerTransaction.Builder()
- .registerFabricatedOverlay(overlay)
- .setSelfTargeting(true);
- overlayManager.commit(transaction.build());
-
- OverlayInfo overlayInfo =
- overlayManager.getOverlayInfosForTarget(OVERLAY_TARGET_PACKAGE_NAME)
- .stream()
- .filter(info -> TextUtils.equals(info.overlayName, OVERLAY_NAME)
- && TextUtils.equals(info.packageName, owningPackage))
- .findFirst()
- .orElse(null);
- if (overlayInfo == null) {
- Log.e(LOG_TAG, "Failed to get overlay info ", new Throwable());
+ byte[] contentBytes = createCompiledResourcesContent(context, colorMapping);
+ if (contentBytes == null) {
return null;
}
- ResourcesLoader colorsLoader = new ResourcesLoader();
- colorsLoader.addProvider(ResourcesProvider.loadOverlay(overlayInfo));
- return new ColorResources(colorsLoader, colorMapping.clone());
- } catch (Exception e) {
- Log.e(LOG_TAG, "Failed to add theme color overlay into loader", e);
+ FileDescriptor arscFile = null;
+ try {
+ arscFile = Os.memfd_create("remote_views_theme_colors.arsc", 0 /* flags */);
+ // Note: This must not be closed through the OutputStream.
+ try (OutputStream pipeWriter = new FileOutputStream(arscFile)) {
+ pipeWriter.write(contentBytes);
+
+ try (ParcelFileDescriptor pfd = ParcelFileDescriptor.dup(arscFile)) {
+ ResourcesLoader colorsLoader = new ResourcesLoader();
+ colorsLoader.addProvider(ResourcesProvider
+ .loadFromTable(pfd, null /* assetsProvider */));
+ return new ColorResources(colorsLoader, colorMapping.clone());
+ }
+ }
+ } finally {
+ if (arscFile != null) {
+ Os.close(arscFile);
+ }
+ }
+ } catch (Exception ex) {
+ Log.e(LOG_TAG, "Failed to setup the context for theme colors", ex);
}
return null;
}
diff --git a/core/java/android/widget/TextClock.java b/core/java/android/widget/TextClock.java
index 255bd679dc35..65cd190725a4 100644
--- a/core/java/android/widget/TextClock.java
+++ b/core/java/android/widget/TextClock.java
@@ -45,6 +45,7 @@ import android.view.inspector.InspectableProperty;
import com.android.internal.R;
import com.android.internal.util.Preconditions;
+import java.time.DateTimeException;
import java.time.Duration;
import java.time.Instant;
import java.time.ZoneId;
@@ -291,11 +292,26 @@ public class TextClock extends TextView {
}
private void createTime(String timeZone) {
- if (timeZone != null) {
- mTime = Calendar.getInstance(TimeZone.getTimeZone(timeZone));
+ TimeZone tz = null;
+ if (timeZone == null) {
+ tz = TimeZone.getDefault();
+ // Note that mTimeZone should always be null if timeZone is.
} else {
- mTime = Calendar.getInstance();
+ tz = TimeZone.getTimeZone(timeZone);
+ try {
+ // Try converting this TZ to a zoneId to make sure it's valid. This
+ // performs a different set of checks than TimeZone.getTimeZone so
+ // we can avoid exceptions later when we do need this conversion.
+ tz.toZoneId();
+ } catch (DateTimeException ex) {
+ // If we're here, the user supplied timezone is invalid, so reset
+ // mTimeZone to something sane.
+ tz = TimeZone.getDefault();
+ mTimeZone = tz.getID();
+ }
}
+
+ mTime = Calendar.getInstance(tz);
}
/**
diff --git a/core/java/android/window/BackNavigationInfo.java b/core/java/android/window/BackNavigationInfo.java
index 6cefc4d2fecf..4b7bacb23e5d 100644
--- a/core/java/android/window/BackNavigationInfo.java
+++ b/core/java/android/window/BackNavigationInfo.java
@@ -87,6 +87,13 @@ public final class BackNavigationInfo implements Parcelable {
*/
public static final String KEY_GESTURE_FINISHED = "GestureFinished";
+ /**
+ * Touch gestured has transferred to embedded window, Shell should pilfer pointers so the
+ * embedded won't receive motion events.
+ * @hide
+ */
+ public static final String KEY_TOUCH_GESTURE_TRANSFERRED = "TouchGestureTransferred";
+
/**
* Defines the type of back destinations a back even can lead to. This is used to define the
@@ -119,7 +126,7 @@ public final class BackNavigationInfo implements Parcelable {
@NonNull
private final Rect mTouchableRegion;
- private final boolean mAppProgressGenerationAllowed;
+ private boolean mAppProgressGenerationAllowed;
private final int mFocusedTaskId;
/**
@@ -253,6 +260,14 @@ public final class BackNavigationInfo implements Parcelable {
}
/**
+ * Force disable app to intercept back progress event.
+ * @hide
+ */
+ public void disableAppProgressGenerationAllowed() {
+ mAppProgressGenerationAllowed = false;
+ }
+
+ /**
* Callback to be called when the back preview is finished in order to notify the server that
* it can clean up the resources created for the animation.
* @hide
diff --git a/core/java/android/window/DesktopModeFlags.java b/core/java/android/window/DesktopModeFlags.java
index 22eec2940e19..dae87ddcb1bd 100644
--- a/core/java/android/window/DesktopModeFlags.java
+++ b/core/java/android/window/DesktopModeFlags.java
@@ -70,7 +70,12 @@ public enum DesktopModeFlags {
ENABLE_DESKTOP_WINDOWING_ENTER_TRANSITIONS(Flags::enableDesktopWindowingTransitions, false),
ENABLE_DESKTOP_WINDOWING_EXIT_TRANSITIONS(Flags::enableDesktopWindowingExitTransitions, false),
ENABLE_WINDOWING_TRANSITION_HANDLERS_OBSERVERS(
- Flags::enableWindowingTransitionHandlersObservers, false);
+ Flags::enableWindowingTransitionHandlersObservers, false),
+ ENABLE_DESKTOP_APP_LAUNCH_ALTTAB_TRANSITIONS(
+ Flags::enableDesktopAppLaunchAlttabTransitions, false),
+ ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS(
+ Flags::enableDesktopAppLaunchTransitions, false),
+ ENABLE_DESKTOP_WINDOWING_PERSISTENCE(Flags::enableDesktopWindowingPersistence, false);
private static final String TAG = "DesktopModeFlagsUtil";
// Function called to obtain aconfig flag value.
diff --git a/core/java/android/window/flags/responsible_apis.aconfig b/core/java/android/window/flags/responsible_apis.aconfig
index 57aacffd4ffc..a2201bb212b0 100644
--- a/core/java/android/window/flags/responsible_apis.aconfig
+++ b/core/java/android/window/flags/responsible_apis.aconfig
@@ -71,6 +71,7 @@ flag {
bug: "339720406"
}
+# replaced by bal_strict_mode_ro
flag {
name: "bal_strict_mode"
namespace: "responsible_apis"
@@ -79,6 +80,14 @@ flag {
}
flag {
+ name: "bal_strict_mode_ro"
+ namespace: "responsible_apis"
+ description: "Strict mode flag"
+ bug: "324089586"
+ is_fixed_read_only: true
+}
+
+flag {
name: "bal_reduce_grace_period"
namespace: "responsible_apis"
description: "Changes to reduce or ideally remove the grace period exemption."
diff --git a/core/java/android/window/flags/windowing_frontend.aconfig b/core/java/android/window/flags/windowing_frontend.aconfig
index ed7a796488af..3a035087be7f 100644
--- a/core/java/android/window/flags/windowing_frontend.aconfig
+++ b/core/java/android/window/flags/windowing_frontend.aconfig
@@ -348,3 +348,14 @@ flag {
bug: "372230928"
is_fixed_read_only: true
}
+
+flag {
+ name: "disallow_app_progress_embedded_window"
+ namespace: "windowing_frontend"
+ description: "Pilfer pointers when app transfer input gesture to embedded window."
+ bug: "365504126"
+ is_fixed_read_only: true
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+} \ No newline at end of file
diff --git a/core/java/com/android/internal/app/SuggestedLocaleAdapter.java b/core/java/com/android/internal/app/SuggestedLocaleAdapter.java
index be3f10acbed3..091975cf855a 100644
--- a/core/java/com/android/internal/app/SuggestedLocaleAdapter.java
+++ b/core/java/com/android/internal/app/SuggestedLocaleAdapter.java
@@ -28,6 +28,7 @@ import android.widget.BaseAdapter;
import android.widget.Filter;
import android.widget.Filterable;
import android.widget.LinearLayout;
+import android.widget.RadioButton;
import android.widget.TextView;
import com.android.internal.R;
@@ -242,11 +243,7 @@ public class SuggestedLocaleAdapter extends BaseAdapter implements Filterable {
break;
case TYPE_SYSTEM_LANGUAGE_FOR_APP_LANGUAGE_PICKER:
TextView title;
- LocaleStore.LocaleInfo info = (LocaleStore.LocaleInfo) getItem(position);
- if (info == null) {
- throw new NullPointerException("Non header locale cannot be null.");
- }
- if (info.isAppCurrentLocale()) {
+ if (mHasSpecificAppPackageName) {
title = itemView.findViewById(R.id.language_picker_item);
} else {
title = itemView.findViewById(R.id.locale);
@@ -254,11 +251,22 @@ public class SuggestedLocaleAdapter extends BaseAdapter implements Filterable {
title.setText(R.string.system_locale_title);
break;
case TYPE_CURRENT_LOCALE:
- updateTextView(itemView,
- itemView.findViewById(R.id.language_picker_item), position);
+ updateTextView(
+ itemView, itemView.findViewById(R.id.language_picker_item), position);
break;
default:
- updateTextView(itemView, itemView.findViewById(R.id.locale), position);
+ LocaleStore.LocaleInfo localeInfo = (LocaleStore.LocaleInfo) getItem(position);
+ if (localeInfo == null) {
+ throw new NullPointerException("Non header locale cannot be null.");
+ }
+ if (mHasSpecificAppPackageName && localeInfo.isSuggested() && !mCountryMode) {
+ updateTextView(
+ itemView,
+ itemView.findViewById(R.id.language_picker_item),
+ position);
+ } else {
+ updateTextView(itemView, itemView.findViewById(R.id.locale), position);
+ }
break;
}
return itemView;
@@ -280,20 +288,21 @@ public class SuggestedLocaleAdapter extends BaseAdapter implements Filterable {
}
break;
case TYPE_SYSTEM_LANGUAGE_FOR_APP_LANGUAGE_PICKER:
- if (((LocaleStore.LocaleInfo) getItem(position)).isAppCurrentLocale()) {
- shouldReuseView = convertView instanceof LinearLayout
- && convertView.findViewById(R.id.language_picker_item) != null;
- if (!shouldReuseView) {
- updatedView = mInflater.inflate(
- R.layout.app_language_picker_current_locale_item,
- parent, false);
+ if (mHasSpecificAppPackageName) {
+ updatedView = mInflater.inflate(
+ R.layout.app_language_picker_locale_item, parent, false);
+ RadioButton option = updatedView.findViewById(R.id.checkbox);
+ if (((LocaleStore.LocaleInfo) getItem(position)).isAppCurrentLocale()) {
+ option.setChecked(true);
+ } else {
+ option.setChecked(false);
}
} else {
shouldReuseView = convertView instanceof TextView
- && convertView.findViewById(R.id.locale) != null;
+ && convertView.findViewById(R.id.locale) != null;
if (!shouldReuseView) {
- updatedView = mInflater.inflate(
- R.layout.language_picker_item, parent, false);
+ updatedView =
+ mInflater.inflate(R.layout.language_picker_item, parent, false);
}
}
break;
@@ -302,14 +311,30 @@ public class SuggestedLocaleAdapter extends BaseAdapter implements Filterable {
&& convertView.findViewById(R.id.language_picker_item) != null;
if (!shouldReuseView) {
updatedView = mInflater.inflate(
- R.layout.app_language_picker_current_locale_item, parent, false);
+ R.layout.app_language_picker_locale_item, parent, false);
+ RadioButton option = updatedView.findViewById(R.id.checkbox);
+ option.setChecked(true);
}
break;
default:
- shouldReuseView = convertView instanceof TextView
+ LocaleStore.LocaleInfo localeInfo = (LocaleStore.LocaleInfo) getItem(position);
+ if (mHasSpecificAppPackageName
+ && localeInfo.isSuggested() && !mCountryMode) {
+ shouldReuseView = convertView instanceof LinearLayout
+ && convertView.findViewById(R.id.language_picker_item) != null;
+ if ((!shouldReuseView)) {
+ updatedView = mInflater.inflate(
+ R.layout.app_language_picker_locale_item, parent, false);
+ RadioButton option = updatedView.findViewById(R.id.checkbox);
+ option.setChecked(false);
+ }
+ } else {
+ shouldReuseView = convertView instanceof TextView
&& convertView.findViewById(R.id.locale) != null;
- if (!shouldReuseView) {
- updatedView = mInflater.inflate(R.layout.language_picker_item, parent, false);
+ if ((!shouldReuseView)) {
+ updatedView = mInflater.inflate(
+ R.layout.language_picker_item, parent, false);
+ }
}
break;
}
diff --git a/core/java/com/android/internal/content/om/OverlayConfig.java b/core/java/com/android/internal/content/om/OverlayConfig.java
index 38593b4a2a99..07e178c0ba27 100644
--- a/core/java/com/android/internal/content/om/OverlayConfig.java
+++ b/core/java/com/android/internal/content/om/OverlayConfig.java
@@ -19,7 +19,6 @@ package com.android.internal.content.om;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.pm.PackagePartitions;
-import android.content.res.AssetManager;
import android.os.Build;
import android.os.Trace;
import android.util.ArrayMap;
@@ -534,7 +533,7 @@ public class OverlayConfig {
*/
@NonNull
public String[] createImmutableFrameworkIdmapsInZygote() {
- final String targetPath = AssetManager.FRAMEWORK_APK_PATH;
+ final String targetPath = "/system/framework/framework-res.apk";
final ArrayList<String> idmapPaths = new ArrayList<>();
final ArrayList<IdmapInvocation> idmapInvocations =
getImmutableFrameworkOverlayIdmapInvocations();
diff --git a/core/java/com/android/internal/content/om/OverlayManagerImpl.java b/core/java/com/android/internal/content/om/OverlayManagerImpl.java
index fa5cf2a396b9..c4624498138d 100644
--- a/core/java/com/android/internal/content/om/OverlayManagerImpl.java
+++ b/core/java/com/android/internal/content/om/OverlayManagerImpl.java
@@ -35,7 +35,6 @@ import android.content.om.OverlayManagerTransaction.Request;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.parsing.FrameworkParsingPackageUtils;
-import android.content.res.AssetManager;
import android.os.FabricatedOverlayInfo;
import android.os.FabricatedOverlayInternal;
import android.os.FabricatedOverlayInternalEntry;
@@ -61,8 +60,8 @@ import java.util.List;
import java.util.Objects;
/**
- * This class provides the functionalities for managing self-targeting overlays, including
- * registering an overlay, unregistering an overlay, and getting the list of overlays information.
+ * This class provides the functionalities of registering an overlay, unregistering an overlay, and
+ * getting the list of overlays information.
*/
public class OverlayManagerImpl {
private static final String TAG = "OverlayManagerImpl";
@@ -235,17 +234,14 @@ 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);
+ 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")) {
- targetPackage = AssetManager.FRAMEWORK_APK_PATH;
- } else {
- targetPackage = Preconditions.checkStringNotEmpty(
- applicationInfo.getBaseCodePath());
- }
-
+ final String 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/jank/FrameTracker.java b/core/java/com/android/internal/jank/FrameTracker.java
index 0af4bea70e65..44c0bd01d545 100644
--- a/core/java/com/android/internal/jank/FrameTracker.java
+++ b/core/java/com/android/internal/jank/FrameTracker.java
@@ -54,6 +54,7 @@ import com.android.internal.util.FrameworkStatsLog;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.List;
import java.util.concurrent.TimeUnit;
/**
@@ -448,7 +449,7 @@ public class FrameTracker implements HardwareRendererObserver.OnFrameMetricsAvai
}
@Override
- public void onJankDataAvailable(SurfaceControl.JankData[] jankData) {
+ public void onJankDataAvailable(List<SurfaceControl.JankData> jankData) {
postCallback(() -> {
try {
Trace.beginSection("FrameTracker#onJankDataAvailable");
@@ -832,7 +833,7 @@ public class FrameTracker implements HardwareRendererObserver.OnFrameMetricsAvai
/** adds the jank listener to the given surface */
public SurfaceControl.OnJankDataListenerRegistration addJankStatsListener(
SurfaceControl.OnJankDataListener listener, SurfaceControl surfaceControl) {
- return surfaceControl.addJankDataListener(listener);
+ return surfaceControl.addOnJankDataListener(listener);
}
}
diff --git a/core/java/com/android/internal/protolog/ProcessedPerfettoProtoLogImpl.java b/core/java/com/android/internal/protolog/ProcessedPerfettoProtoLogImpl.java
index febe1f3a72ac..70d148a311f6 100644
--- a/core/java/com/android/internal/protolog/ProcessedPerfettoProtoLogImpl.java
+++ b/core/java/com/android/internal/protolog/ProcessedPerfettoProtoLogImpl.java
@@ -132,9 +132,9 @@ public class ProcessedPerfettoProtoLogImpl extends PerfettoProtoLogImpl {
@Deprecated
@Override
void dumpViewerConfig() {
- Log.d(LOG_TAG, "Dumping viewer config to trace");
+ Log.d(LOG_TAG, "Dumping viewer config to trace from " + mViewerConfigFilePath);
Utils.dumpViewerConfig(mDataSource, mViewerConfigInputStreamProvider);
- Log.d(LOG_TAG, "Dumped viewer config to trace");
+ Log.d(LOG_TAG, "Successfully dumped viewer config to trace from " + mViewerConfigFilePath);
}
@NonNull
diff --git a/core/java/com/android/server/FgThread.java b/core/java/com/android/server/FgThread.java
index f54ee5e70265..f8a6bb06798e 100644
--- a/core/java/com/android/server/FgThread.java
+++ b/core/java/com/android/server/FgThread.java
@@ -33,6 +33,7 @@ import java.util.concurrent.Executor;
*
* @hide
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class FgThread extends ServiceThread {
private static final long SLOW_DISPATCH_THRESHOLD_MS = 100;
private static final long SLOW_DELIVERY_THRESHOLD_MS = 200;
diff --git a/core/java/com/android/server/ServiceThread.java b/core/java/com/android/server/ServiceThread.java
index 0eff1fca1e25..86e507b76771 100644
--- a/core/java/com/android/server/ServiceThread.java
+++ b/core/java/com/android/server/ServiceThread.java
@@ -27,6 +27,7 @@ import android.os.StrictMode;
*
* @hide
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class ServiceThread extends HandlerThread {
private static final String TAG = "ServiceThread";
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 2283b88e5d97..2bb6e71e9000 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -105,18 +105,7 @@ cc_library_shared_for_libandroid_runtime {
],
shared_libs: [
- "libbase",
- "libcutils",
"libtracing_perfetto",
- "libharfbuzz_ng",
- "liblog",
- "libmediautils",
- "libminikin",
- "libz",
- "server_configurable_flags",
- "libaconfig_storage_read_api_cc",
- "android.database.sqlite-aconfig-cc",
- "android.media.audiopolicy-aconfig-cc",
],
static_libs: [
@@ -303,6 +292,14 @@ cc_library_shared_for_libandroid_runtime {
],
shared_libs: [
+ "libbase",
+ "libharfbuzz_ng",
+ "liblog",
+ "libmediautils",
+ "libminikin",
+ "libz",
+ "android.database.sqlite-aconfig-cc",
+ "android.media.audiopolicy-aconfig-cc",
"audioclient-types-aidl-cpp",
"audioflinger-aidl-cpp",
"audiopolicy-types-aidl-cpp",
@@ -412,20 +409,24 @@ cc_library_shared_for_libandroid_runtime {
"frameworks/native/libs/nativebase/include",
"frameworks/native/libs/nativewindow/include",
],
- shared_libs: [
- "libicui18n",
- "libicuuc",
- ],
static_libs: [
"libandroidfw",
+ "libbase",
"libbinary_parse",
+ "libcutils",
"libdng_sdk",
"libft2",
+ "libharfbuzz_ng",
"libhostgraphics",
"libhwui",
+ "libicui18n",
+ "libicuuc",
+ "libicuuc_stubdata",
"libimage_type_recognition",
"libinput",
"libjpeg",
+ "liblog",
+ "libminikin",
"libnativehelper_jvm",
"libpiex",
"libpng",
@@ -435,11 +436,18 @@ cc_library_shared_for_libandroid_runtime {
"libwebp-decode",
"libwebp-encode",
"libwuffs_mirror_release_c",
+ "libz",
"libimage_io",
"libjpegdecoder",
"libjpegencoder",
"libultrahdr",
+ "server_configurable_flags",
+ ],
+ export_static_lib_headers: [
+ "libnativehelper_jvm",
+ "libui-types",
],
+ stl: "libc++_static",
},
host_linux: {
srcs: [
@@ -465,14 +473,18 @@ cc_library_shared_for_libandroid_runtime {
"libbinderthreadstateutils",
"libsqlite",
"libgui_window_info_static",
- ],
- shared_libs: [
- // libbinder needs to be shared since it has global state
- // (e.g. gDefaultServiceManager)
"libbinder",
"libhidlbase", // libhwbinder is in here
],
},
+ linux_glibc_x86_64: {
+ ldflags: ["-static-libgcc"],
+ dist: {
+ targets: ["layoutlib"],
+ dir: "layoutlib_native/linux",
+ tag: "stripped_all",
+ },
+ },
},
}
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 7fefe1755fa9..df87a69f02ce 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -298,6 +298,11 @@ static struct {
jmethodID ctor;
} gFrameRateCategoryRateClassInfo;
+static struct {
+ jclass clazz;
+ jmethodID asList;
+} gUtilArrays;
+
constexpr ui::Dataspace pickDataspaceFromColorMode(const ui::ColorMode colorMode) {
switch (colorMode) {
case ui::ColorMode::DISPLAY_P3:
@@ -2206,10 +2211,13 @@ public:
env->SetObjectArrayElement(jJankDataArray, i, jJankData);
env->DeleteLocalRef(jJankData);
}
- env->CallVoidMethod(target,
- gJankDataListenerClassInfo.onJankDataAvailable,
- jJankDataArray);
+
+ jobject jJankDataList =
+ env->CallStaticObjectMethod(gUtilArrays.clazz, gUtilArrays.asList, jJankDataArray);
env->DeleteLocalRef(jJankDataArray);
+
+ env->CallVoidMethod(target, gJankDataListenerClassInfo.onJankDataAvailable, jJankDataList);
+ env->DeleteLocalRef(jJankDataList);
env->DeleteLocalRef(target);
return true;
@@ -2858,7 +2866,7 @@ int register_android_view_SurfaceControl(JNIEnv* env)
gJankDataListenerClassInfo.clazz = MakeGlobalRefOrDie(env, onJankDataListenerClazz);
gJankDataListenerClassInfo.onJankDataAvailable =
GetMethodIDOrDie(env, onJankDataListenerClazz, "onJankDataAvailable",
- "([Landroid/view/SurfaceControl$JankData;)V");
+ "(Ljava/util/List;)V");
jclass transactionCommittedListenerClazz =
FindClassOrDie(env, "android/view/SurfaceControl$TransactionCommittedListener");
@@ -2933,6 +2941,10 @@ int register_android_view_SurfaceControl(JNIEnv* env)
gStalledTransactionInfoClassInfo.frameNumber =
GetFieldIDOrDie(env, stalledTransactionInfoClazz, "frameNumber", "J");
+ jclass utilArrays = FindClassOrDie(env, "java/util/Arrays");
+ gUtilArrays.clazz = MakeGlobalRefOrDie(env, utilArrays);
+ gUtilArrays.asList = GetStaticMethodIDOrDie(env, utilArrays, "asList",
+ "([Ljava/lang/Object;)Ljava/util/List;");
return err;
}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 561d351ede90..5044a300cc8e 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -7023,7 +7023,6 @@
<!-- Allows an application to set the advanced features on BiometricDialog (SystemUI), including
logo, logo description, and content view with more options button.
<p>Not for use by third-party applications.
- @FlaggedApi("android.hardware.biometrics.custom_biometric_prompt")
-->
<permission android:name="android.permission.SET_BIOMETRIC_DIALOG_ADVANCED"
android:protectionLevel="signature|privileged" />
@@ -8536,10 +8535,14 @@
@hide
-->
<permission
- android:name="android.permission.RECEIVE_PACKAGE_CHANGED_BROADCAST_ON_COMPONENT_STATE_CHANGED"
+ android:name="android.permission.INTERNAL_RECEIVE_PACKAGE_CHANGED_BROADCAST_ON_COMPONENT_STATE_CHANGED"
android:protectionLevel="internal"
android:featureFlag="android.content.pm.reduce_broadcasts_for_component_state_changes"/>
+ <uses-permission
+ android:name="android.permission.INTERNAL_RECEIVE_PACKAGE_CHANGED_BROADCAST_ON_COMPONENT_STATE_CHANGED"
+ android:featureFlag="android.content.pm.reduce_broadcasts_for_component_state_changes"/>
+
<!-- Attribution for Geofencing service. -->
<attribution android:tag="GeofencingService" android:label="@string/geofencing_service"/>
<!-- Attribution for Country Detector. -->
diff --git a/core/res/res/layout/app_language_picker_current_locale_item.xml b/core/res/res/layout/app_language_picker_locale_item.xml
index edd6d648ea99..bcad9ce751bc 100644
--- a/core/res/res/layout/app_language_picker_current_locale_item.xml
+++ b/core/res/res/layout/app_language_picker_locale_item.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- ~ Copyright (C) 2022 The Android Open Source Project
+ ~ Copyright (C) 2024 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
@@ -20,10 +20,27 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical">
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:gravity="center"
+ android:minHeight="?android:attr/listPreferredItemHeight"
+ android:layout_marginStart="20dip">
+
+ <RadioButton
+ android:id="@+id/checkbox"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:background="@null"
+ android:focusable="false"
+ android:clickable="false" />
+
+ </LinearLayout>
+
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_marginEnd="6dip"
android:layout_marginTop="6dip"
android:layout_marginBottom="6dip"
android:layout_weight="1">
@@ -31,20 +48,4 @@
android:id="@+id/language_picker_item"
layout="@layout/language_picker_item" />
</RelativeLayout>
-
- <LinearLayout
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:gravity="center"
- android:minHeight="?android:attr/listPreferredItemHeight">
- <ImageView
- android:id="@+id/imageView"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_centerVertical="true"
- android:layout_marginHorizontal="16dp"
- android:src="@drawable/ic_check_24dp"
- app:tint="?attr/colorAccentPrimaryVariant"
- android:contentDescription="@*android:string/checked"/>
- </LinearLayout>
</LinearLayout>
diff --git a/core/res/res/values-en-rCA/strings.xml b/core/res/res/values-en-rCA/strings.xml
index ef420eb0a35a..3fac7360c8f2 100644
--- a/core/res/res/values-en-rCA/strings.xml
+++ b/core/res/res/values-en-rCA/strings.xml
@@ -1943,8 +1943,7 @@
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Off"</string>
<string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
<string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> - <xliff:g id="END">%2$s</xliff:g>"</string>
- <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
- <skip />
+ <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g> to <xliff:g id="END">%2$s</xliff:g>"</string>
<string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Any calendar"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> is muting some sounds"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"There\'s an internal problem with your device, and it may be unstable until you factory data reset."</string>
@@ -2426,14 +2425,10 @@
<string name="satellite_notification_manual_summary" msgid="901206289846283445">"Send and receive messages without a mobile or Wi-Fi network"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Open Messages"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"How it works"</string>
- <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
- <skip />
- <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
- <skip />
- <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
- <skip />
- <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
- <skip />
+ <string name="satellite_manual_selection_state_popup_title" msgid="8545991934926661974">"Turn on \"Automatically select network\""</string>
+ <string name="satellite_manual_selection_state_popup_message" msgid="1928101658551382450">"Turn on \"Automatically select network\" in Settings so your phone can find a network that works with satellite"</string>
+ <string name="satellite_manual_selection_state_popup_ok" msgid="2459664752624985095">"Turn on"</string>
+ <string name="satellite_manual_selection_state_popup_cancel" msgid="973605633339469252">"Go back"</string>
<string name="unarchival_session_app_label" msgid="6811856981546348205">"Pending..."</string>
<string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Set up Fingerprint Unlock again"</string>
<string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"<xliff:g id="FINGERPRINT">%s</xliff:g> can no longer be recognized."</string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index 051c61a26373..4070f1160aec 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -1944,8 +1944,7 @@
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Off"</string>
<string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
<string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> - <xliff:g id="END">%2$s</xliff:g>"</string>
- <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
- <skip />
+ <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"Da <xliff:g id="START">%1$s</xliff:g> a <xliff:g id="END">%2$s</xliff:g>"</string>
<string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Qualsiasi calendario"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> sta disattivando alcuni suoni"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"Si è verificato un problema interno con il dispositivo, che potrebbe essere instabile fino al ripristino dei dati di fabbrica."</string>
@@ -2427,14 +2426,10 @@
<string name="satellite_notification_manual_summary" msgid="901206289846283445">"Invia e ricevi messaggi senza una rete mobile o Wi-Fi"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Apri Messaggi"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Come funziona"</string>
- <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
- <skip />
- <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
- <skip />
- <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
- <skip />
- <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
- <skip />
+ <string name="satellite_manual_selection_state_popup_title" msgid="8545991934926661974">"Attiva \"Seleziona rete automaticamente\""</string>
+ <string name="satellite_manual_selection_state_popup_message" msgid="1928101658551382450">"Attiva \"Seleziona rete automaticamente\" nelle Impostazioni in modo che lo smartphone possa trovare una rete compatibile con il satellite"</string>
+ <string name="satellite_manual_selection_state_popup_ok" msgid="2459664752624985095">"Attiva"</string>
+ <string name="satellite_manual_selection_state_popup_cancel" msgid="973605633339469252">"Indietro"</string>
<string name="unarchival_session_app_label" msgid="6811856981546348205">"In attesa…"</string>
<string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Riconfigura lo Sblocco con l\'Impronta"</string>
<string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"<xliff:g id="FINGERPRINT">%s</xliff:g> non può più essere riconosciuto."</string>
diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml
index 2fea96252ebc..5684d780be8f 100644
--- a/core/res/res/values-ne/strings.xml
+++ b/core/res/res/values-ne/strings.xml
@@ -590,7 +590,7 @@
<string name="permdesc_changeWifiMulticastState" product="default" msgid="8296627590220222740">"तपाईँको फोन मात्र होइन, मल्टिकास्ट ठेगानाहरूको प्रयोग गरे Wi-Fi नेटवर्कका सबै उपकरणहरूमा पठाइएका प्याकेटहरू प्राप्त गर्न एपलाई अनुमति दिन्छ। यसले गैर-मल्टिकास्ट मोडभन्दा बढी उर्जा प्रयोग गर्छ।"</string>
<string name="permlab_bluetoothAdmin" msgid="6490373569441946064">"ब्लुटुथ सेटिङहरूमा पहुँच गर्नुहोस्"</string>
<string name="permdesc_bluetoothAdmin" product="tablet" msgid="5370837055438574863">"स्थानीय ब्लुटुथ ट्याब्लेटलाई कन्फिगर गर्नको लागि र टाढाका उपकरणहरूलाई पत्ता लगाउन र जोड्नको लागि एपलाई अनुमति दिन्छ।"</string>
- <string name="permdesc_bluetoothAdmin" product="tv" msgid="1623992984547014588">"एपलाई तपाईंको Android टिभी डिभाइसको ब्लुटुथ कन्फिगर गर्ने तथा टाढा रहेका यन्त्रहरू पत्ता लगाई ती यन्त्रहरूसँग जोडा बनाउने अनुमति दिन्छ।"</string>
+ <string name="permdesc_bluetoothAdmin" product="tv" msgid="1623992984547014588">"एपलाई तपाईंको Android टिभी डिभाइसको ब्लुटुथ कन्फिगर गर्ने तथा टाढा रहेका यन्त्रहरू पत्ता लगाई ती यन्त्रहरूसँग कनेक्ट गर्ने अनुमति दिन्छ।"</string>
<string name="permdesc_bluetoothAdmin" product="default" msgid="7381341743021234863">"एपलाई स्थानीय ब्लुटुथ फोन कन्फिगर गर्न र टाढाका उपकरणहरूसँग खोज गर्न र जोडी गर्न अनुमति दिन्छ।"</string>
<string name="permlab_accessWimaxState" msgid="7029563339012437434">"WiMAXसँग जोड्नुहोस् वा छुटाउनुहोस्"</string>
<string name="permdesc_accessWimaxState" msgid="5372734776802067708">"एपलाई वाइम्याक्स सक्षम छ कि छैन र जडान भएको कुनै पनि वाइम्याक्स नेटवर्कहरूको बारेमा जानकारी निर्धारिण गर्न अनुमति दिन्छ।"</string>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index 388c5a9077ae..86e0fcdad8a1 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -1944,8 +1944,7 @@
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Desativada"</string>
<string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
<string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> – <xliff:g id="END">%2$s</xliff:g>"</string>
- <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
- <skip />
+ <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g> – <xliff:g id="END">%2$s</xliff:g>"</string>
<string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Qualquer calendário"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> está a desativar alguns sons."</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"Existe um problema interno no seu dispositivo e pode ficar instável até efetuar uma reposição de dados de fábrica."</string>
@@ -2427,14 +2426,10 @@
<string name="satellite_notification_manual_summary" msgid="901206289846283445">"Envie e receba mensagens sem uma rede móvel ou Wi-Fi"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Abre a app Mensagens"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Como funciona"</string>
- <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
- <skip />
- <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
- <skip />
- <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
- <skip />
- <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
- <skip />
+ <string name="satellite_manual_selection_state_popup_title" msgid="8545991934926661974">"Ative a opção \"Selecionar rede automaticamente\""</string>
+ <string name="satellite_manual_selection_state_popup_message" msgid="1928101658551382450">"Ative a opção \"Selecionar rede automaticamente\" nas Definições para que o telemóvel possa encontrar uma rede que funcione com o satélite"</string>
+ <string name="satellite_manual_selection_state_popup_ok" msgid="2459664752624985095">"Ativar"</string>
+ <string name="satellite_manual_selection_state_popup_cancel" msgid="973605633339469252">"Retroceder"</string>
<string name="unarchival_session_app_label" msgid="6811856981546348205">"Pendente…"</string>
<string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Configure o Desbloqueio por impressão digital novamente"</string>
<string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"Já não é possível reconhecer <xliff:g id="FINGERPRINT">%s</xliff:g>."</string>
diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml
index 492dca723c21..1d9e00c012bb 100644
--- a/core/res/res/values-te/strings.xml
+++ b/core/res/res/values-te/strings.xml
@@ -1937,7 +1937,7 @@
<string name="zen_mode_default_weeknights_name" msgid="7902108149994062847">"వారపు రోజుల్లో రాత్రి"</string>
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"వారాంతం"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"ఈవెంట్"</string>
- <string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"నిద్ర"</string>
+ <string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"స్లీప్ మోడ్"</string>
<string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"<xliff:g id="APP_NAME">%1$s</xliff:g> ద్వారా మేనేజ్ చేయబడుతోంది"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"ఆన్‌లో ఉంది"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"ఆఫ్‌లో ఉంది"</string>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index d750ff6623f2..4c9da6959fe2 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -3486,6 +3486,12 @@
representation this attribute can be used for providing such. -->
<attr name="contentDescription" format="string" localization="suggested" />
+ <!-- Provides brief supplemental information for the view, such as the purpose of
+ the view when that purpose is not conveyed within its textual representation.
+ This property is used primarily for accessibility. -->
+ <!-- @FlaggedApi("android.view.accessibility.supplemental_description") -->
+ <attr name="supplementalDescription" format="string" localization="suggested" />
+
<!-- Sets the id of a view that screen readers are requested to visit after this view.
Requests that a screen-reader visits the content of this view before the content of the
one it precedes. This does nothing if either view is not important for accessibility.
diff --git a/core/res/res/values/public-staging.xml b/core/res/res/values/public-staging.xml
index 81215453b3ef..09dc08420a01 100644
--- a/core/res/res/values/public-staging.xml
+++ b/core/res/res/values/public-staging.xml
@@ -121,6 +121,8 @@
<!-- @FlaggedApi("android.permission.flags.replace_body_sensor_permission_enabled")
@hide @SystemApi -->
<public name="backgroundPermission"/>
+ <!-- @FlaggedApi(android.view.accessibility.supplemental_description) -->
+ <public name="supplementalDescription"/>
</staging-public-group>
<staging-public-group type="id" first-id="0x01b60000">
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index b32d4cf17cf5..515ebd54b5eb 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -5264,7 +5264,7 @@
<java-symbol type="string" name="system_locale_title" />
<java-symbol type="layout" name="app_language_picker_system_default" />
<java-symbol type="layout" name="app_language_picker_system_current" />
- <java-symbol type="layout" name="app_language_picker_current_locale_item" />
+ <java-symbol type="layout" name="app_language_picker_locale_item" />
<java-symbol type="id" name="system_locale_subtitle" />
<java-symbol type="id" name="language_picker_item" />
<java-symbol type="id" name="language_picker_header" />
diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java
index 930e03dfd6db..bef65d42ba79 100644
--- a/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java
+++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java
@@ -46,7 +46,7 @@ public class AccessibilityNodeInfoTest {
// The number of fields tested in the corresponding CTS AccessibilityNodeInfoTest:
// See fullyPopulateAccessibilityNodeInfo, assertEqualsAccessibilityNodeInfo,
// and assertAccessibilityNodeInfoCleared in that class.
- private static final int NUM_MARSHALLED_PROPERTIES = 46;
+ private static final int NUM_MARSHALLED_PROPERTIES = 47;
/**
* The number of properties that are purposely not marshalled
diff --git a/core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java b/core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java
index 60b5a422ea80..e7a6cb7291c5 100644
--- a/core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java
+++ b/core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java
@@ -67,6 +67,7 @@ import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Mockito;
+import java.util.Arrays;
import java.util.concurrent.TimeUnit;
@SmallTest
@@ -690,10 +691,9 @@ public class FrameTrackerTest {
FrameTracker tracker, long durationMillis, long vsyncId, @JankType int jankType) {
final ArgumentCaptor<Runnable> captor = ArgumentCaptor.forClass(Runnable.class);
doNothing().when(tracker).postCallback(captor.capture());
- mListenerCapture.getValue().onJankDataAvailable(new JankData[] {
- new JankData(vsyncId, jankType, FRAME_TIME_60Hz, FRAME_TIME_60Hz,
- TimeUnit.MILLISECONDS.toNanos(durationMillis))
- });
+ mListenerCapture.getValue().onJankDataAvailable(Arrays.asList(new JankData(
+ vsyncId, jankType, FRAME_TIME_60Hz, FRAME_TIME_60Hz,
+ TimeUnit.MILLISECONDS.toNanos(durationMillis))));
captor.getValue().run();
}
}
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..40d0bef2fb0f 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
@@ -210,6 +210,21 @@ public class OverlayManagerImplTest {
}
@Test
+ 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/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java
index 68d8ebbbdabf..b7a1c13c75c7 100644
--- a/graphics/java/android/graphics/Paint.java
+++ b/graphics/java/android/graphics/Paint.java
@@ -18,6 +18,8 @@ package android.graphics;
import static com.android.text.flags.Flags.FLAG_FIX_LINE_HEIGHT_FOR_LOCALE;
import static com.android.text.flags.Flags.FLAG_LETTER_SPACING_JUSTIFICATION;
+import static com.android.text.flags.Flags.FLAG_DEPRECATE_ELEGANT_TEXT_HEIGHT_API;
+
import android.annotation.ColorInt;
import android.annotation.ColorLong;
@@ -39,6 +41,7 @@ import android.text.GraphicsOperations;
import android.text.SpannableString;
import android.text.SpannedString;
import android.text.TextUtils;
+import android.util.Log;
import com.android.internal.annotations.GuardedBy;
import com.android.text.flags.Flags;
@@ -61,6 +64,7 @@ import java.util.Objects;
* geometries, text and bitmaps.
*/
public class Paint {
+ private static final String TAG = "Paint";
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private long mNativePaint;
@@ -1803,8 +1807,18 @@ public class Paint {
/**
* Get the elegant metrics flag.
*
+ * Note:
+ * For applications target API 35 or later, this function returns true by default.
+ * For applications target API 36 or later, the function call will be ignored and the elegant
+ * text height is always enabled.
+ *
* @return true if elegant metrics are enabled for text drawing.
+ * @deprecated The underlying UI fonts are deprecated and will be removed from the system image.
+ * Applications supporting scripts with large vertical metrics should adapt their UI by using
+ * fonts designed with corresponding vertical metrics.
*/
+ @Deprecated
+ @FlaggedApi(FLAG_DEPRECATE_ELEGANT_TEXT_HEIGHT_API)
public boolean isElegantTextHeight() {
return nGetElegantTextHeight(mNativePaint) != ELEGANT_TEXT_HEIGHT_DISABLED;
}
@@ -1819,9 +1833,28 @@ public class Paint {
* variants that have not been compacted to fit Latin-based vertical
* metrics, and also increases top and bottom bounds to provide more space.
*
+ * <p>
+ * Note:
+ * For applications target API 35 or later, the default value will be true by default.
+ * For applications target API 36 or later, the function call will be ignored and the elegant
+ * text height is always enabled.
+ *
* @param elegant set the paint's elegant metrics flag for drawing text.
+ * @deprecated This API will be no-op at some point in the future. The underlying UI fonts is
+ * deprecated and will be removed from the system image. Applications supporting scripts with
+ * large vertical metrics should adapt their UI by using fonts designed with corresponding
+ * vertical metrics.
*/
+ @Deprecated
+ @FlaggedApi(FLAG_DEPRECATE_ELEGANT_TEXT_HEIGHT_API)
public void setElegantTextHeight(boolean elegant) {
+ if (Flags.deprecateElegantTextHeightApi() && !elegant
+ && CompatChanges.isChangeEnabled(DEPRECATE_UI_FONT_ENFORCE)) {
+ if (!elegant) {
+ Log.w(TAG, "The elegant text height cannot be turned off.");
+ }
+ return;
+ }
nSetElegantTextHeight(mNativePaint,
elegant ? ELEGANT_TEXT_HEIGHT_ENABLED : ELEGANT_TEXT_HEIGHT_DISABLED);
}
@@ -1839,6 +1872,19 @@ public class Paint {
@EnabledSince(targetSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM)
public static final long DEPRECATE_UI_FONT = 279646685L;
+ /**
+ * A change ID for deprecating UI fonts enforced.
+ *
+ * From API 36, the elegant text height will not be able to be overridden and always true if the
+ * app has a target SDK of API 36 or later.
+ *
+ * @hide
+ */
+ @ChangeId
+ @EnabledSince(targetSdkVersion = 36)
+ public static final long DEPRECATE_UI_FONT_ENFORCE = 349519475L;
+
+
private void resetElegantTextHeight() {
if (CompatChanges.isChangeEnabled(DEPRECATE_UI_FONT)) {
nSetElegantTextHeight(mNativePaint, ELEGANT_TEXT_HEIGHT_UNSET);
diff --git a/graphics/java/android/graphics/RenderNode.java b/graphics/java/android/graphics/RenderNode.java
index 211f74a47bdd..03a8b306f99d 100644
--- a/graphics/java/android/graphics/RenderNode.java
+++ b/graphics/java/android/graphics/RenderNode.java
@@ -279,7 +279,8 @@ public final class RenderNode {
* @hide
*/
default void positionChanged(long frameNumber, int left, int top, int right, int bottom,
- int clipLeft, int clipTop, int clipRight, int clipBottom) {
+ int clipLeft, int clipTop, int clipRight, int clipBottom,
+ int nodeWidth, int nodeHeight) {
positionChanged(frameNumber, left, top, right, bottom);
}
@@ -304,11 +305,12 @@ public final class RenderNode {
* @hide */
static boolean callPositionChanged2(WeakReference<PositionUpdateListener> weakListener,
long frameNumber, int left, int top, int right, int bottom,
- int clipLeft, int clipTop, int clipRight, int clipBottom) {
+ int clipLeft, int clipTop, int clipRight, int clipBottom,
+ int nodeWidth, int nodeHeight) {
final PositionUpdateListener listener = weakListener.get();
if (listener != null) {
listener.positionChanged(frameNumber, left, top, right, bottom, clipLeft,
- clipTop, clipRight, clipBottom);
+ clipTop, clipRight, clipBottom, nodeWidth, nodeHeight);
return true;
} else {
return false;
@@ -401,10 +403,11 @@ public final class RenderNode {
@Override
public void positionChanged(long frameNumber, int left, int top, int right, int bottom,
- int clipLeft, int clipTop, int clipRight, int clipBottom) {
+ int clipLeft, int clipTop, int clipRight, int clipBottom,
+ int nodeWidth, int nodeHeight) {
for (PositionUpdateListener pul : mListeners) {
pul.positionChanged(frameNumber, left, top, right, bottom, clipLeft, clipTop,
- clipRight, clipBottom);
+ clipRight, clipBottom, nodeWidth, nodeHeight);
}
}
diff --git a/graphics/java/android/graphics/RuntimeColorFilter.java b/graphics/java/android/graphics/RuntimeColorFilter.java
new file mode 100644
index 000000000000..52724ceaf301
--- /dev/null
+++ b/graphics/java/android/graphics/RuntimeColorFilter.java
@@ -0,0 +1,305 @@
+/*
+ * 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.graphics;
+
+import android.annotation.ColorInt;
+import android.annotation.ColorLong;
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+
+import com.android.graphics.hwui.flags.Flags;
+
+
+/**
+ * <p>A {@link RuntimeColorFilter} calculates a per-pixel color based on the output of a user
+ * * defined Android Graphics Shading Language (AGSL) function.</p>
+ *
+ * <p>This AGSL function takes in an input color to be operated on. This color is in sRGB and the
+ * * output is also interpreted as sRGB. The AGSL function signature expects a single input
+ * * of color (packed as a half4 or float4 or vec4).</p>
+ *
+ * <pre class="prettyprint">
+ * vec4 main(half4 in_color);
+ * </pre>
+ */
+@FlaggedApi(Flags.FLAG_RUNTIME_COLOR_FILTERS_BLENDERS)
+public class RuntimeColorFilter extends ColorFilter {
+
+ private String mAgsl;
+
+ /**
+ * Creates a new RuntimeColorFilter.
+ *
+ * @param agsl The text of AGSL color filter program to run.
+ */
+ public RuntimeColorFilter(@NonNull String agsl) {
+ if (agsl == null) {
+ throw new NullPointerException("RuntimeColorFilter requires a non-null AGSL string");
+ }
+ mAgsl = agsl;
+ // call to parent class to register native RuntimeColorFilter
+ // TODO: find way to get super class to create native instance without requiring the storage
+ // of agsl string
+ getNativeInstance();
+
+ }
+ /**
+ * Sets the uniform color value corresponding to this color filter. If the effect does not have
+ * a uniform with that name or if the uniform is declared with a type other than vec3 or vec4
+ * and corresponding layout(color) annotation then an IllegalArgumentException is thrown.
+ *
+ * @param uniformName name matching the color uniform declared in the AGSL program
+ * @param color the provided sRGB color
+ */
+ public void setColorUniform(@NonNull String uniformName, @ColorInt int color) {
+ setUniform(uniformName, Color.valueOf(color).getComponents(), true);
+ }
+
+ /**
+ * Sets the uniform color value corresponding to this color filter. If the effect does not have
+ * a uniform with that name or if the uniform is declared with a type other than vec3 or vec4
+ * and corresponding layout(color) annotation then an IllegalArgumentException is thrown.
+ *
+ * @param uniformName name matching the color uniform declared in the AGSL program
+ * @param color the provided sRGB color
+ */
+ public void setColorUniform(@NonNull String uniformName, @ColorLong long color) {
+ Color exSRGB = Color.valueOf(color).convert(ColorSpace.get(ColorSpace.Named.EXTENDED_SRGB));
+ setUniform(uniformName, exSRGB.getComponents(), true);
+ }
+
+ /**
+ * Sets the uniform color value corresponding to this color filter. If the effect does not have
+ * a uniform with that name or if the uniform is declared with a type other than vec3 or vec4
+ * and corresponding layout(color) annotation then an IllegalArgumentException is thrown.
+ *
+ * @param uniformName name matching the color uniform declared in the AGSL program
+ * @param color the provided sRGB color
+ */
+ public void setColorUniform(@NonNull String uniformName, @NonNull Color color) {
+ if (color == null) {
+ throw new NullPointerException("The color parameter must not be null");
+ }
+ Color exSRGB = color.convert(ColorSpace.get(ColorSpace.Named.EXTENDED_SRGB));
+ setUniform(uniformName, exSRGB.getComponents(), true);
+ }
+
+ /**
+ * Sets the uniform value corresponding to this color filter. If the effect does not have a
+ * uniform with that name or if the uniform is declared with a type other than a float or
+ * float[1] then an IllegalArgumentException is thrown.
+ *
+ * @param uniformName name matching the uniform declared in the AGSL program
+ */
+ public void setFloatUniform(@NonNull String uniformName, float value) {
+ setFloatUniform(uniformName, value, 0.0f, 0.0f, 0.0f, 1);
+ }
+
+ /**
+ * Sets the uniform value corresponding to this color filter. If the effect does not have a
+ * uniform with that name or if the uniform is declared with a type other than a vec2 or
+ * float[2] then an IllegalArgumentException is thrown.
+ *
+ * @param uniformName name matching the uniform declared in the AGSL program
+ */
+ public void setFloatUniform(@NonNull String uniformName, float value1, float value2) {
+ setFloatUniform(uniformName, value1, value2, 0.0f, 0.0f, 2);
+ }
+
+ /**
+ * Sets the uniform value corresponding to this color filter. If the effect does not have a
+ * uniform with that name or if the uniform is declared with a type other than a vec3 or
+ * float[3] then an IllegalArgumentException is thrown.
+ *
+ * @param uniformName name matching the uniform declared in the AGSL program
+ */
+ public void setFloatUniform(@NonNull String uniformName, float value1, float value2,
+ float value3) {
+ setFloatUniform(uniformName, value1, value2, value3, 0.0f, 3);
+
+ }
+
+ /**
+ * Sets the uniform value corresponding to this color filter. If the effect does not have a
+ * uniform with that name or if the uniform is declared with a type other than a vec4 or
+ * float[4] then an IllegalArgumentException is thrown.
+ *
+ * @param uniformName name matching the uniform declared in the AGSL program
+ */
+ public void setFloatUniform(@NonNull String uniformName, float value1, float value2,
+ float value3, float value4) {
+ setFloatUniform(uniformName, value1, value2, value3, value4, 4);
+ }
+
+ /**
+ * Sets the uniform value corresponding to this color filter. If the effect does not have a
+ * uniform with that name or if the uniform is declared with a type other than a float
+ * (for N=1), vecN, or float[N] where N is the length of the values param then an
+ * IllegalArgumentException is thrown.
+ *
+ * @param uniformName name matching the uniform declared in the AGSL program
+ */
+ public void setFloatUniform(@NonNull String uniformName, @NonNull float[] values) {
+ setUniform(uniformName, values, false);
+ }
+
+ private void setFloatUniform(@NonNull String uniformName, float value1, float value2,
+ float value3, float value4, int count) {
+ if (uniformName == null) {
+ throw new NullPointerException("The uniformName parameter must not be null");
+ }
+ nativeUpdateUniforms(getNativeInstance(), uniformName, value1, value2, value3, value4,
+ count);
+ }
+
+ private void setUniform(@NonNull String uniformName, @NonNull float[] values, boolean isColor) {
+ if (uniformName == null) {
+ throw new NullPointerException("The uniformName parameter must not be null");
+ }
+ if (values == null) {
+ throw new NullPointerException("The uniform values parameter must not be null");
+ }
+ nativeUpdateUniforms(getNativeInstance(), uniformName, values, isColor);
+ }
+
+ /**
+ * Sets the uniform value corresponding to this color filter. If the effect does not have a
+ * uniform with that name or if the uniform is declared with a type other than an int or int[1]
+ * then an IllegalArgumentException is thrown.
+ *
+ * @param uniformName name matching the uniform declared in the AGSL program
+ */
+ public void setIntUniform(@NonNull String uniformName, int value) {
+ setIntUniform(uniformName, value, 0, 0, 0, 1);
+ }
+
+ /**
+ * Sets the uniform value corresponding to this color filter. If the effect does not have a
+ * uniform with that name or if the uniform is declared with a type other than an ivec2 or
+ * int[2] then an IllegalArgumentException is thrown.
+ *
+ * @param uniformName name matching the uniform declared in the AGSL program
+ */
+ public void setIntUniform(@NonNull String uniformName, int value1, int value2) {
+ setIntUniform(uniformName, value1, value2, 0, 0, 2);
+ }
+
+ /**
+ * Sets the uniform value corresponding to this color filter. If the effect does not have a
+ * uniform with that name or if the uniform is declared with a type other than an ivec3 or
+ * int[3] then an IllegalArgumentException is thrown.
+ *
+ * @param uniformName name matching the uniform declared in the AGSL program
+ */
+ public void setIntUniform(@NonNull String uniformName, int value1, int value2, int value3) {
+ setIntUniform(uniformName, value1, value2, value3, 0, 3);
+ }
+
+ /**
+ * Sets the uniform value corresponding to this color filter. If the effect does not have a
+ * uniform with that name or if the uniform is declared with a type other than an ivec4 or
+ * int[4] then an IllegalArgumentException is thrown.
+ *
+ * @param uniformName name matching the uniform declared in the AGSL program
+ */
+ public void setIntUniform(@NonNull String uniformName, int value1, int value2,
+ int value3, int value4) {
+ setIntUniform(uniformName, value1, value2, value3, value4, 4);
+ }
+
+ /**
+ * Sets the uniform value corresponding to this color filter. If the effect does not have a
+ * uniform with that name or if the uniform is declared with a type other than an int (for N=1),
+ * ivecN, or int[N] where N is the length of the values param then an IllegalArgumentException
+ * is thrown.
+ *
+ * @param uniformName name matching the uniform declared in the AGSL program
+ */
+ public void setIntUniform(@NonNull String uniformName, @NonNull int[] values) {
+ if (uniformName == null) {
+ throw new NullPointerException("The uniformName parameter must not be null");
+ }
+ if (values == null) {
+ throw new NullPointerException("The uniform values parameter must not be null");
+ }
+ nativeUpdateUniforms(getNativeInstance(), uniformName, values);
+ }
+
+ private void setIntUniform(@NonNull String uniformName, int value1, int value2, int value3,
+ int value4, int count) {
+ if (uniformName == null) {
+ throw new NullPointerException("The uniformName parameter must not be null");
+ }
+ nativeUpdateUniforms(getNativeInstance(), uniformName, value1, value2, value3, value4,
+ count);
+ }
+
+ /**
+ * Assigns the uniform shader to the provided shader parameter. If the shader program does not
+ * have a uniform shader with that name then an IllegalArgumentException is thrown.
+ *
+ * @param shaderName name matching the uniform declared in the AGSL program
+ * @param shader shader passed into the AGSL program for sampling
+ */
+ public void setInputShader(@NonNull String shaderName, @NonNull Shader shader) {
+ if (shaderName == null) {
+ throw new NullPointerException("The shaderName parameter must not be null");
+ }
+ if (shader == null) {
+ throw new NullPointerException("The shader parameter must not be null");
+ }
+ nativeUpdateChild(getNativeInstance(), shaderName, shader.getNativeInstance());
+ }
+
+ /**
+ * Assigns the uniform color filter to the provided color filter parameter. If the shader
+ * program does not have a uniform color filter with that name then an IllegalArgumentException
+ * is thrown.
+ *
+ * @param filterName name matching the uniform declared in the AGSL program
+ * @param colorFilter filter passed into the AGSL program for sampling
+ */
+ public void setInputColorFilter(@NonNull String filterName, @NonNull ColorFilter colorFilter) {
+ if (filterName == null) {
+ throw new NullPointerException("The filterName parameter must not be null");
+ }
+ if (colorFilter == null) {
+ throw new NullPointerException("The colorFilter parameter must not be null");
+ }
+ nativeUpdateChild(getNativeInstance(), filterName, colorFilter.getNativeInstance());
+ }
+
+ /** @hide */
+ @Override
+ protected long createNativeInstance() {
+ return nativeCreateRuntimeColorFilter(mAgsl);
+ }
+
+ private static native long nativeCreateRuntimeColorFilter(String agsl);
+ private static native void nativeUpdateUniforms(
+ long colorFilter, String uniformName, float[] uniforms, boolean isColor);
+ private static native void nativeUpdateUniforms(
+ long colorFilter, String uniformName, float value1, float value2, float value3,
+ float value4, int count);
+ private static native void nativeUpdateUniforms(
+ long colorFilter, String uniformName, int[] uniforms);
+ private static native void nativeUpdateUniforms(
+ long colorFilter, String uniformName, int value1, int value2, int value3,
+ int value4, int count);
+ private static native void nativeUpdateChild(long colorFilter, String childName, long child);
+
+}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/DividerPresenter.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/DividerPresenter.java
index ad194f707cf3..6398c7a2f498 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/DividerPresenter.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/DividerPresenter.java
@@ -39,6 +39,7 @@ import static androidx.window.extensions.embedding.SplitPresenter.CONTAINER_POSI
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
+import android.annotation.ColorInt;
import android.annotation.Nullable;
import android.app.Activity;
import android.app.ActivityThread;
@@ -1394,10 +1395,14 @@ class DividerPresenter implements View.OnTouchListener {
}
private void showVeils(@NonNull SurfaceControl.Transaction t) {
- final Color primaryVeilColor = getContainerBackgroundColor(
- mProperties.mPrimaryContainer, DEFAULT_PRIMARY_VEIL_COLOR);
- final Color secondaryVeilColor = getContainerBackgroundColor(
- mProperties.mSecondaryContainer, DEFAULT_SECONDARY_VEIL_COLOR);
+ final Color primaryVeilColor = getVeilColor(
+ mProperties.mDividerAttributes.getPrimaryVeilColor(),
+ mProperties.mPrimaryContainer,
+ DEFAULT_PRIMARY_VEIL_COLOR);
+ final Color secondaryVeilColor = getVeilColor(
+ mProperties.mDividerAttributes.getSecondaryVeilColor(),
+ mProperties.mSecondaryContainer,
+ DEFAULT_SECONDARY_VEIL_COLOR);
t.setColor(mPrimaryVeil, colorToFloatArray(primaryVeilColor))
.setColor(mSecondaryVeil, colorToFloatArray(secondaryVeilColor))
.setLayer(mDividerSurface, DIVIDER_LAYER)
@@ -1444,6 +1449,21 @@ class DividerPresenter implements View.OnTouchListener {
}
}
+ /**
+ * Returns the veil color.
+ *
+ * If the configured color is not transparent, we use the configured color, otherwise we use
+ * the window background color of the top activity. If the background color of the top
+ * activity is unavailable, the default color is used.
+ */
+ @NonNull
+ private static Color getVeilColor(@ColorInt int configuredColor,
+ @NonNull TaskFragmentContainer container, @NonNull Color defaultColor) {
+ return configuredColor != Color.TRANSPARENT
+ ? Color.valueOf(configuredColor)
+ : getContainerBackgroundColor(container, defaultColor);
+ }
+
private static float[] colorToFloatArray(@NonNull Color color) {
return new float[]{color.red(), color.green(), color.blue()};
}
diff --git a/libs/WindowManager/Shell/multivalentScreenshotTests/Android.bp b/libs/WindowManager/Shell/multivalentScreenshotTests/Android.bp
index 61c09f2c396b..7f54c75929fb 100644
--- a/libs/WindowManager/Shell/multivalentScreenshotTests/Android.bp
+++ b/libs/WindowManager/Shell/multivalentScreenshotTests/Android.bp
@@ -22,42 +22,6 @@ package {
default_team: "trendy_team_multitasking_windowing",
}
-android_app {
- name: "WMShellRobolectricScreenshotTestApp",
- platform_apis: true,
- certificate: "platform",
- static_libs: [
- "WindowManager-Shell",
- "platform-screenshot-diff-core",
- "ScreenshotComposeUtilsLib", // ComposableScreenshotTestRule & Theme.PlatformUi.Screenshot
- "SystemUI-res", // Theme.SystemUI (dragged in by ScreenshotComposeUtilsLib)
- ],
- asset_dirs: ["goldens/robolectric"],
- manifest: "AndroidManifestRobolectric.xml",
- use_resource_processor: true,
-}
-
-android_robolectric_test {
- name: "WMShellRobolectricScreenshotTests",
- instrumentation_for: "WMShellRobolectricScreenshotTestApp",
- upstream: true,
- java_resource_dirs: [
- "robolectric/config",
- ],
- srcs: [
- "src/**/*.kt",
- ],
- static_libs: [
- "junit",
- "androidx.test.runner",
- "androidx.test.rules",
- "androidx.test.ext.junit",
- "truth",
- "platform-parametric-runner-lib",
- ],
- auto_gen_config: true,
-}
-
android_test {
name: "WMShellMultivalentScreenshotTestsOnDevice",
srcs: [
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 52ce8cb5c0dc..0b515f590f98 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
@@ -23,14 +23,15 @@ import android.content.res.Resources
import android.graphics.Color
import android.graphics.drawable.Icon
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.view.IWindowManager
import android.view.WindowManager
-import android.view.WindowManagerGlobal
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import androidx.test.platform.app.InstrumentationRegistry
+import com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn
import com.android.internal.logging.testing.UiEventLoggerFake
import com.android.internal.protolog.ProtoLog
import com.android.launcher3.icons.BubbleIconFactory
@@ -41,6 +42,7 @@ 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
@@ -51,9 +53,7 @@ import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.kotlin.mock
-import android.platform.test.annotations.DisableFlags
-import android.platform.test.annotations.EnableFlags
-import com.android.wm.shell.shared.bubbles.BubbleBarLocation
+import org.mockito.kotlin.verify
import java.util.concurrent.Semaphore
import java.util.concurrent.TimeUnit
import java.util.function.Consumer
@@ -72,7 +72,7 @@ class BubbleStackViewTest {
private lateinit var expandedViewManager: FakeBubbleExpandedViewManager
private lateinit var bubbleStackView: BubbleStackView
private lateinit var shellExecutor: ShellExecutor
- private lateinit var windowManager: IWindowManager
+ private lateinit var windowManager: WindowManager
private lateinit var bubbleTaskViewFactory: BubbleTaskViewFactory
private lateinit var bubbleData: BubbleData
private lateinit var bubbleStackViewManager: FakeBubbleStackViewManager
@@ -83,9 +83,8 @@ class BubbleStackViewTest {
PhysicsAnimatorTestUtils.prepareForTest()
// Disable protolog tool when running the tests from studio
ProtoLog.REQUIRE_PROTOLOGTOOL = false
- windowManager = WindowManagerGlobal.getWindowManagerService()!!
shellExecutor = TestShellExecutor()
- val windowManager = context.getSystemService(WindowManager::class.java)
+ windowManager = context.getSystemService(WindowManager::class.java)
iconFactory =
BubbleIconFactory(
context,
@@ -354,6 +353,16 @@ class BubbleStackViewTest {
assertThat(bubbleStackView.getBubbleIndex(bubbleOverflow)).isGreaterThan(-1)
}
+ @Test
+ fun removeFromWindow_stopMonitoringSwipeUpGesture() {
+ spyOn(bubbleStackView)
+ InstrumentationRegistry.getInstrumentation().runOnMainSync {
+ // No way to add to window in the test environment right now so just pretend
+ bubbleStackView.onDetachedFromWindow()
+ }
+ verify(bubbleStackView).stopMonitoringSwipeUpGesture()
+ }
+
private fun createAndInflateChatBubble(key: String): Bubble {
val icon = Icon.createWithResource(context.resources, R.drawable.bubble_ic_overflow_button)
val shortcutInfo = ShortcutInfo.Builder(context, "fakeId").setIcon(icon).build()
diff --git a/libs/WindowManager/Shell/res/layout/compat_ui_layout.xml b/libs/WindowManager/Shell/res/layout/compat_ui_layout.xml
index 62782a784db9..e7ead63a8df6 100644
--- a/libs/WindowManager/Shell/res/layout/compat_ui_layout.xml
+++ b/libs/WindowManager/Shell/res/layout/compat_ui_layout.xml
@@ -22,14 +22,14 @@
android:gravity="bottom|end">
<include android:id="@+id/size_compat_hint"
- android:visibility="gone"
+ android:visibility="invisible"
android:layout_width="@dimen/compat_hint_width"
android:layout_height="wrap_content"
layout="@layout/compat_mode_hint"/>
<ImageButton
android:id="@+id/size_compat_restart_button"
- android:visibility="gone"
+ android:visibility="invisible"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/compat_button_margin"
diff --git a/libs/WindowManager/Shell/res/layout/user_aspect_ratio_settings_layout.xml b/libs/WindowManager/Shell/res/layout/user_aspect_ratio_settings_layout.xml
index 433d8546ece0..b5f04c3b815a 100644
--- a/libs/WindowManager/Shell/res/layout/user_aspect_ratio_settings_layout.xml
+++ b/libs/WindowManager/Shell/res/layout/user_aspect_ratio_settings_layout.xml
@@ -22,14 +22,14 @@
android:gravity="bottom|end">
<include android:id="@+id/user_aspect_ratio_settings_hint"
- android:visibility="gone"
+ android:visibility="invisible"
android:layout_width="@dimen/compat_hint_width"
android:layout_height="wrap_content"
layout="@layout/compat_mode_hint"/>
<ImageButton
android:id="@+id/user_aspect_ratio_settings_button"
- android:visibility="gone"
+ android:visibility="invisible"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/compat_button_margin"
diff --git a/libs/WindowManager/Shell/res/values-ar/strings.xml b/libs/WindowManager/Shell/res/values-ar/strings.xml
index c36783f8f488..a8febc80ffc1 100644
--- a/libs/WindowManager/Shell/res/values-ar/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ar/strings.xml
@@ -133,10 +133,8 @@
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"تكبير الشاشة إلى أقصى حدّ"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"التقاط صورة للشاشة"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"لا يمكن نقل التطبيق إلى هنا"</string>
- <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
- <skip />
- <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
- <skip />
+ <string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"مجسَّم"</string>
+ <string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"استعادة"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"تكبير"</string>
<string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"استعادة"</string>
<string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"المحاذاة إلى اليسار"</string>
diff --git a/libs/WindowManager/Shell/res/values-as/strings.xml b/libs/WindowManager/Shell/res/values-as/strings.xml
index 71d95edccea6..8c924e342875 100644
--- a/libs/WindowManager/Shell/res/values-as/strings.xml
+++ b/libs/WindowManager/Shell/res/values-as/strings.xml
@@ -133,10 +133,8 @@
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"স্ক্ৰীন মেক্সিমাইজ কৰক"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"স্ক্ৰীন স্নেপ কৰক"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"ইয়ালৈ এপ্‌টো আনিব নোৱাৰি"</string>
- <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
- <skip />
- <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
- <skip />
+ <string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"ইমাৰ্ছিভ"</string>
+ <string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"পুনঃস্থাপন কৰক"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"মেক্সিমাইজ কৰক"</string>
<string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"পুনঃস্থাপন কৰক"</string>
<string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"বাওঁফাললৈ স্নেপ কৰক"</string>
diff --git a/libs/WindowManager/Shell/res/values-bn/strings.xml b/libs/WindowManager/Shell/res/values-bn/strings.xml
index 4412497c3862..22a445f1754c 100644
--- a/libs/WindowManager/Shell/res/values-bn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-bn/strings.xml
@@ -133,10 +133,8 @@
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"স্ক্রিন বড় করুন"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"স্ক্রিনে অ্যাপ মানানসই হিসেবে ছোট বড় করুন"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"অ্যাপটি এখানে সরানো যাবে না"</string>
- <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
- <skip />
- <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
- <skip />
+ <string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"ইমারসিভ"</string>
+ <string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"ফিরিয়ে আনুন"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"বড় করুন"</string>
<string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"ফিরিয়ে আনুন"</string>
<string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"বাঁদিকে স্ন্যাপ করুন"</string>
diff --git a/libs/WindowManager/Shell/res/values-bs/strings.xml b/libs/WindowManager/Shell/res/values-bs/strings.xml
index 674022be39a2..73f30d797883 100644
--- a/libs/WindowManager/Shell/res/values-bs/strings.xml
+++ b/libs/WindowManager/Shell/res/values-bs/strings.xml
@@ -133,10 +133,8 @@
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maksimiziraj ekran"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Snimi ekran"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Ne možete premjestiti aplikaciju ovdje"</string>
- <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
- <skip />
- <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
- <skip />
+ <string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"Interaktivno"</string>
+ <string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"Vrati"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maksimiziranje"</string>
<string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Vraćanje"</string>
<string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Pomicanje ulijevo"</string>
diff --git a/libs/WindowManager/Shell/res/values-cs/strings.xml b/libs/WindowManager/Shell/res/values-cs/strings.xml
index fc6b679a3a09..6a5780e01822 100644
--- a/libs/WindowManager/Shell/res/values-cs/strings.xml
+++ b/libs/WindowManager/Shell/res/values-cs/strings.xml
@@ -133,10 +133,8 @@
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximalizovat obrazovku"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Rozpůlit obrazovku"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Aplikaci sem nelze přesunout"</string>
- <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
- <skip />
- <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
- <skip />
+ <string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"Pohlcující"</string>
+ <string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"Obnovit"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maximalizovat"</string>
<string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Obnovit"</string>
<string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Přichytit vlevo"</string>
diff --git a/libs/WindowManager/Shell/res/values-el/strings.xml b/libs/WindowManager/Shell/res/values-el/strings.xml
index d06b46fb40d4..d02fae2a986d 100644
--- a/libs/WindowManager/Shell/res/values-el/strings.xml
+++ b/libs/WindowManager/Shell/res/values-el/strings.xml
@@ -133,10 +133,8 @@
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Μεγιστοποίηση οθόνης"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Προβολή στο μισό της οθόνης"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Δεν είναι δυνατή η μετακίνηση της εφαρμογής εδώ"</string>
- <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
- <skip />
- <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
- <skip />
+ <string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"Καθηλωτικό"</string>
+ <string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"Επαναφορά"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Μεγιστοποίηση"</string>
<string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Επαναφορά"</string>
<string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Κούμπωμα αριστερά"</string>
diff --git a/libs/WindowManager/Shell/res/values-en-rAU/strings.xml b/libs/WindowManager/Shell/res/values-en-rAU/strings.xml
index c1a4244896c5..f9911451f4b5 100644
--- a/libs/WindowManager/Shell/res/values-en-rAU/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rAU/strings.xml
@@ -133,10 +133,8 @@
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximise screen"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Snap screen"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"App can\'t be moved here"</string>
- <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
- <skip />
- <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
- <skip />
+ <string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"Immersive"</string>
+ <string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"Restore"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maximise"</string>
<string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Restore"</string>
<string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Snap left"</string>
diff --git a/libs/WindowManager/Shell/res/values-en-rGB/strings.xml b/libs/WindowManager/Shell/res/values-en-rGB/strings.xml
index c1a4244896c5..f9911451f4b5 100644
--- a/libs/WindowManager/Shell/res/values-en-rGB/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rGB/strings.xml
@@ -133,10 +133,8 @@
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximise screen"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Snap screen"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"App can\'t be moved here"</string>
- <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
- <skip />
- <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
- <skip />
+ <string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"Immersive"</string>
+ <string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"Restore"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maximise"</string>
<string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Restore"</string>
<string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Snap left"</string>
diff --git a/libs/WindowManager/Shell/res/values-en-rIN/strings.xml b/libs/WindowManager/Shell/res/values-en-rIN/strings.xml
index c1a4244896c5..f9911451f4b5 100644
--- a/libs/WindowManager/Shell/res/values-en-rIN/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rIN/strings.xml
@@ -133,10 +133,8 @@
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximise screen"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Snap screen"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"App can\'t be moved here"</string>
- <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
- <skip />
- <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
- <skip />
+ <string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"Immersive"</string>
+ <string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"Restore"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maximise"</string>
<string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Restore"</string>
<string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Snap left"</string>
diff --git a/libs/WindowManager/Shell/res/values-hi/strings.xml b/libs/WindowManager/Shell/res/values-hi/strings.xml
index ff675a9f6356..527793eac9c3 100644
--- a/libs/WindowManager/Shell/res/values-hi/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hi/strings.xml
@@ -133,10 +133,8 @@
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"स्क्रीन को बड़ा करें"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"स्नैप स्क्रीन"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"ऐप्लिकेशन को यहां मूव नहीं किया जा सकता"</string>
- <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
- <skip />
- <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
- <skip />
+ <string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"इमर्सिव"</string>
+ <string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"वापस लाएं"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"बड़ा करें"</string>
<string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"पहले जैसा करें"</string>
<string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"बाईं ओर स्नैप करें"</string>
diff --git a/libs/WindowManager/Shell/res/values-hr/strings.xml b/libs/WindowManager/Shell/res/values-hr/strings.xml
index 16fbcd0565fa..659d1ec39b73 100644
--- a/libs/WindowManager/Shell/res/values-hr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hr/strings.xml
@@ -133,10 +133,8 @@
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maksimalno povećaj zaslon"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Izradi snimku zaslona"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Aplikacija se ne može premjestiti ovdje"</string>
- <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
- <skip />
- <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
- <skip />
+ <string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"Interaktivno"</string>
+ <string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"Vrati"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maksimiziraj"</string>
<string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Vrati"</string>
<string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Poravnaj lijevo"</string>
diff --git a/libs/WindowManager/Shell/res/values-is/strings.xml b/libs/WindowManager/Shell/res/values-is/strings.xml
index f595765a797e..ca1bc15edf33 100644
--- a/libs/WindowManager/Shell/res/values-is/strings.xml
+++ b/libs/WindowManager/Shell/res/values-is/strings.xml
@@ -133,10 +133,8 @@
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Stækka skjá"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Smelluskjár"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Ekki er hægt að færa forritið hingað"</string>
- <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
- <skip />
- <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
- <skip />
+ <string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"Umlykjandi"</string>
+ <string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"Endurheimta"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Stækka"</string>
<string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Endurheimta"</string>
<string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Smella til vinstri"</string>
diff --git a/libs/WindowManager/Shell/res/values-it/strings.xml b/libs/WindowManager/Shell/res/values-it/strings.xml
index 8b7c15ffeb15..87919b5dc1ff 100644
--- a/libs/WindowManager/Shell/res/values-it/strings.xml
+++ b/libs/WindowManager/Shell/res/values-it/strings.xml
@@ -133,10 +133,8 @@
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Massimizza schermo"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Aggancia schermo"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Impossibile spostare l\'app qui"</string>
- <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
- <skip />
- <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
- <skip />
+ <string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"Immersivo"</string>
+ <string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"Ripristina"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Ingrandisci"</string>
<string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Ripristina"</string>
<string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Aggancia a sinistra"</string>
diff --git a/libs/WindowManager/Shell/res/values-ja/strings.xml b/libs/WindowManager/Shell/res/values-ja/strings.xml
index 5464cfa28b3c..c7a77d9b9214 100644
--- a/libs/WindowManager/Shell/res/values-ja/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ja/strings.xml
@@ -133,10 +133,8 @@
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"画面の最大化"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"画面のスナップ"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"アプリはここに移動できません"</string>
- <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
- <skip />
- <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
- <skip />
+ <string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"没入モード"</string>
+ <string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"復元"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"最大化"</string>
<string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"復元"</string>
<string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"左にスナップ"</string>
diff --git a/libs/WindowManager/Shell/res/values-ka/strings.xml b/libs/WindowManager/Shell/res/values-ka/strings.xml
index 7ea9daffca18..39362ef4ca57 100644
--- a/libs/WindowManager/Shell/res/values-ka/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ka/strings.xml
@@ -133,10 +133,8 @@
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"აპლიკაციის გაშლა სრულ ეკრანზე"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"აპლიკაციის დაპატარავება ეკრანზე"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"აპის აქ გადატანა შეუძლებელია"</string>
- <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
- <skip />
- <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
- <skip />
+ <string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"იმერსიული"</string>
+ <string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"აღდგენა"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"მაქსიმალურად გაშლა"</string>
<string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"აღდგენა"</string>
<string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"მარცხნივ გადატანა"</string>
diff --git a/libs/WindowManager/Shell/res/values-km/strings.xml b/libs/WindowManager/Shell/res/values-km/strings.xml
index 83bd3744d27a..9c4ae05f3a36 100644
--- a/libs/WindowManager/Shell/res/values-km/strings.xml
+++ b/libs/WindowManager/Shell/res/values-km/strings.xml
@@ -133,10 +133,8 @@
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"ពង្រីកអេក្រង់"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"ថតអេក្រង់"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"មិនអាចផ្លាស់ទីកម្មវិធីមកទីនេះបានទេ"</string>
- <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
- <skip />
- <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
- <skip />
+ <string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"ជក់ចិត្ត"</string>
+ <string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"ស្ដារ"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"ពង្រីក"</string>
<string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"ស្ដារ"</string>
<string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"ផ្លាស់ទីទៅឆ្វេង"</string>
diff --git a/libs/WindowManager/Shell/res/values-kn/strings.xml b/libs/WindowManager/Shell/res/values-kn/strings.xml
index 502e0c92f844..f365cfb34412 100644
--- a/libs/WindowManager/Shell/res/values-kn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-kn/strings.xml
@@ -133,10 +133,8 @@
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"ಸ್ಕ್ರೀನ್ ಅನ್ನು ಮ್ಯಾಕ್ಸಿಮೈಸ್ ಮಾಡಿ"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"ಸ್ನ್ಯಾಪ್ ಸ್ಕ್ರೀನ್"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"ಆ್ಯಪ್ ಅನ್ನು ಇಲ್ಲಿಗೆ ಸರಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ"</string>
- <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
- <skip />
- <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
- <skip />
+ <string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"ಇಮ್ಮರ್ಸಿವ್"</string>
+ <string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"ಮರುಸ್ಥಾಪಿಸಿ"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"ಮ್ಯಾಕ್ಸಿಮೈಸ್ ಮಾಡಿ"</string>
<string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"ಮರುಸ್ಥಾಪಿಸಿ"</string>
<string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"ಎಡಕ್ಕೆ ಸ್ನ್ಯಾಪ್ ಮಾಡಿ"</string>
diff --git a/libs/WindowManager/Shell/res/values-lt/strings.xml b/libs/WindowManager/Shell/res/values-lt/strings.xml
index 4551f72eb635..5a7f58e5781a 100644
--- a/libs/WindowManager/Shell/res/values-lt/strings.xml
+++ b/libs/WindowManager/Shell/res/values-lt/strings.xml
@@ -133,10 +133,8 @@
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Išskleisti ekraną"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Sutraukti ekraną"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Programos negalima perkelti čia"</string>
- <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
- <skip />
- <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
- <skip />
+ <string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"Įtraukiantis"</string>
+ <string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"Atkurti"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Padidinti"</string>
<string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Atkurti"</string>
<string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Pritraukti kairėje"</string>
diff --git a/libs/WindowManager/Shell/res/values-mr/strings.xml b/libs/WindowManager/Shell/res/values-mr/strings.xml
index a2c610e82823..871bc3fcc8e7 100644
--- a/libs/WindowManager/Shell/res/values-mr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-mr/strings.xml
@@ -133,10 +133,8 @@
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"स्क्रीन मोठी करा"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"स्क्रीन स्नॅप करा"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"अ‍ॅप इथे हलवू शकत नाही"</string>
- <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
- <skip />
- <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
- <skip />
+ <string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"इमर्सिव्ह"</string>
+ <string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"रिस्टोअर करा"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"मोठे करा"</string>
<string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"रिस्टोअर करा"</string>
<string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"डावीकडे स्नॅप करा"</string>
diff --git a/libs/WindowManager/Shell/res/values-ms/strings.xml b/libs/WindowManager/Shell/res/values-ms/strings.xml
index 2102a382d232..71666cae93c8 100644
--- a/libs/WindowManager/Shell/res/values-ms/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ms/strings.xml
@@ -133,10 +133,8 @@
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maksimumkan Skrin"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Tangkap Skrin"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Apl tidak boleh dialihkan ke sini"</string>
- <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
- <skip />
- <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
- <skip />
+ <string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"Mengasyikkan"</string>
+ <string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"Pulihkan"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maksimumkan"</string>
<string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Pulihkan"</string>
<string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Autojajar ke kiri"</string>
diff --git a/libs/WindowManager/Shell/res/values-ne/strings.xml b/libs/WindowManager/Shell/res/values-ne/strings.xml
index e9a530621c7e..7015b2c11b32 100644
--- a/libs/WindowManager/Shell/res/values-ne/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ne/strings.xml
@@ -133,10 +133,8 @@
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"स्क्रिन ठुलो बनाउनुहोस्"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"स्क्रिन स्न्याप गर्नुहोस्"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"एप सारेर यहाँ ल्याउन सकिएन"</string>
- <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
- <skip />
- <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
- <skip />
+ <string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"इमर्सिभ"</string>
+ <string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"रिस्टोर गर्नुहोस्"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"ठुलो बनाउनुहोस्"</string>
<string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"रिस्टोर गर्नुहोस्"</string>
<string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"बायाँतिर स्न्याप गर्नुहोस्"</string>
diff --git a/libs/WindowManager/Shell/res/values-pl/strings.xml b/libs/WindowManager/Shell/res/values-pl/strings.xml
index 14b7a09f6143..5f78b134ad22 100644
--- a/libs/WindowManager/Shell/res/values-pl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pl/strings.xml
@@ -133,10 +133,8 @@
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maksymalizuj ekran"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Przyciągnij ekran"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Nie można przenieść aplikacji tutaj"</string>
- <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
- <skip />
- <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
- <skip />
+ <string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"Tryb immersyjny"</string>
+ <string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"Przywróć"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maksymalizuj"</string>
<string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Przywróć"</string>
<string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Przyciągnij do lewej"</string>
diff --git a/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml b/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml
index 9631c45a8097..cd78ef95d88e 100644
--- a/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml
@@ -133,10 +133,8 @@
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximizar ecrã"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Encaixar ecrã"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Não é possível mover a app para aqui"</string>
- <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
- <skip />
- <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
- <skip />
+ <string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"Envolvente"</string>
+ <string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"Restaurar"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maximizar"</string>
<string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Restaurar"</string>
<string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Encaixar à esquerda"</string>
diff --git a/libs/WindowManager/Shell/res/values-sl/strings.xml b/libs/WindowManager/Shell/res/values-sl/strings.xml
index 2012c8a6754e..ae7e524da6cc 100644
--- a/libs/WindowManager/Shell/res/values-sl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sl/strings.xml
@@ -133,10 +133,8 @@
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maksimiraj zaslon"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Pripni zaslon"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Aplikacije ni mogoče premakniti sem"</string>
- <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
- <skip />
- <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
- <skip />
+ <string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"Poglobljeno"</string>
+ <string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"Obnovi"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maksimiraj"</string>
<string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Obnovi"</string>
<string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Pripni levo"</string>
diff --git a/libs/WindowManager/Shell/res/values-ta/strings.xml b/libs/WindowManager/Shell/res/values-ta/strings.xml
index c4243f7ae90d..2c73d3a14620 100644
--- a/libs/WindowManager/Shell/res/values-ta/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ta/strings.xml
@@ -133,10 +133,8 @@
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"திரையைப் பெரிதாக்கு"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"திரையை ஸ்னாப் செய்"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"ஆப்ஸை இங்கே நகர்த்த முடியாது"</string>
- <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
- <skip />
- <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
- <skip />
+ <string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"ஈடுபட வைக்கும்"</string>
+ <string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"மீட்டெடுக்கும்"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"பெரிதாக்கும்"</string>
<string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"மீட்டெடுக்கும்"</string>
<string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"இடதுபுறம் நகர்த்தும்"</string>
diff --git a/libs/WindowManager/Shell/res/values-te/strings.xml b/libs/WindowManager/Shell/res/values-te/strings.xml
index 47fc48ddc3c3..b17d4d1afaf7 100644
--- a/libs/WindowManager/Shell/res/values-te/strings.xml
+++ b/libs/WindowManager/Shell/res/values-te/strings.xml
@@ -133,10 +133,8 @@
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"స్క్రీన్ సైజ్‌ను పెంచండి"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"స్క్రీన్‌ను స్నాప్ చేయండి"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"యాప్‌ను ఇక్కడకి తరలించడం సాధ్యం కాదు"</string>
- <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
- <skip />
- <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
- <skip />
+ <string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"లీనమయ్యే"</string>
+ <string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"రీస్టోర్ చేయండి"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"మ్యాగ్జిమైజ్ చేయండి"</string>
<string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"రీస్టోర్ చేయండి"</string>
<string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"ఎడమ వైపున స్నాప్ చేయండి"</string>
diff --git a/libs/WindowManager/Shell/res/values-th/strings.xml b/libs/WindowManager/Shell/res/values-th/strings.xml
index 260807537385..43cee41f5a15 100644
--- a/libs/WindowManager/Shell/res/values-th/strings.xml
+++ b/libs/WindowManager/Shell/res/values-th/strings.xml
@@ -133,10 +133,8 @@
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"ขยายหน้าจอให้ใหญ่สุด"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"สแนปหน้าจอ"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"ย้ายแอปมาที่นี่ไม่ได้"</string>
- <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
- <skip />
- <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
- <skip />
+ <string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"สมจริง"</string>
+ <string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"คืนค่า"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"ขยายใหญ่สุด"</string>
<string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"คืนค่า"</string>
<string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"จัดพอดีกับทางซ้าย"</string>
diff --git a/libs/WindowManager/Shell/res/values-tl/strings.xml b/libs/WindowManager/Shell/res/values-tl/strings.xml
index 94166ef5db5e..428499532005 100644
--- a/libs/WindowManager/Shell/res/values-tl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-tl/strings.xml
@@ -133,10 +133,8 @@
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"I-maximize ang Screen"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"I-snap ang Screen"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Hindi mailipat dito ang app"</string>
- <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
- <skip />
- <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
- <skip />
+ <string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"Immersive"</string>
+ <string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"I-restore"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"I-maximize"</string>
<string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"I-restore"</string>
<string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"I-snap pakaliwa"</string>
diff --git a/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml b/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml
index ad50a045c924..2fb3f5ab5ea4 100644
--- a/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml
@@ -133,10 +133,8 @@
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"最大化屏幕"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"屏幕快照"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"无法将应用移至此处"</string>
- <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
- <skip />
- <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
- <skip />
+ <string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"沉浸式"</string>
+ <string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"恢复"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"最大化"</string>
<string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"恢复"</string>
<string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"贴靠左侧"</string>
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/pip/PipContentOverlay.java b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/pip/PipContentOverlay.java
index eb7ef1478a90..62ca5c687a2a 100644
--- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/pip/PipContentOverlay.java
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/pip/PipContentOverlay.java
@@ -18,6 +18,7 @@ package com.android.wm.shell.shared.pip;
import static android.util.TypedValue.COMPLEX_UNIT_DIP;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.content.res.TypedArray;
@@ -170,26 +171,34 @@ public abstract class PipContentOverlay {
private final Context mContext;
private final int mAppIconSizePx;
- private final Rect mAppBounds;
+ /**
+ * The bounds of the application window relative to the task leash.
+ */
+ private final Rect mRelativeAppBounds;
private final int mOverlayHalfSize;
private final Matrix mTmpTransform = new Matrix();
private final float[] mTmpFloat9 = new float[9];
private Bitmap mBitmap;
- public PipAppIconOverlay(Context context, Rect appBounds, Rect destinationBounds,
- Drawable appIcon, int appIconSizePx) {
+ // TODO(b/356277166): add non-match_parent support on PIP2.
+ /**
+ * @param context the {@link Context} that contains the icon information
+ * @param relativeAppBounds the bounds of the app window frame relative to the task leash
+ * @param destinationBounds the bounds for rhe PIP task
+ * @param appIcon the app icon {@link Drawable}
+ * @param appIconSizePx the icon dimension in pixel
+ */
+ public PipAppIconOverlay(@NonNull Context context, @NonNull Rect relativeAppBounds,
+ @NonNull Rect destinationBounds, @NonNull Drawable appIcon, int appIconSizePx) {
mContext = context;
final int maxAppIconSizePx = (int) TypedValue.applyDimension(COMPLEX_UNIT_DIP,
MAX_APP_ICON_SIZE_DP, context.getResources().getDisplayMetrics());
mAppIconSizePx = Math.min(maxAppIconSizePx, appIconSizePx);
- final int overlaySize = getOverlaySize(appBounds, destinationBounds);
+ final int overlaySize = getOverlaySize(relativeAppBounds, destinationBounds);
mOverlayHalfSize = overlaySize >> 1;
-
- // When the activity is in the secondary split, make sure the scaling center is not
- // offset.
- mAppBounds = new Rect(0, 0, appBounds.width(), appBounds.height());
+ mRelativeAppBounds = relativeAppBounds;
mBitmap = Bitmap.createBitmap(overlaySize, overlaySize, Bitmap.Config.ARGB_8888);
prepareAppIconOverlay(appIcon);
@@ -206,9 +215,9 @@ public abstract class PipContentOverlay {
* the overlay will be drawn with the max size of the start and end bounds in different
* rotation.
*/
- public static int getOverlaySize(Rect appBounds, Rect destinationBounds) {
- final int appWidth = appBounds.width();
- final int appHeight = appBounds.height();
+ public static int getOverlaySize(Rect overlayBounds, Rect destinationBounds) {
+ final int appWidth = overlayBounds.width();
+ final int appHeight = overlayBounds.height();
return Math.max(Math.max(appWidth, appHeight),
Math.max(destinationBounds.width(), destinationBounds.height())) + 1;
@@ -230,15 +239,15 @@ public abstract class PipContentOverlay {
mTmpTransform.reset();
// In order for the overlay to always cover the pip window, the overlay may have a
// size larger than the pip window. Make sure that app icon is at the center.
- final int appBoundsCenterX = mAppBounds.centerX();
- final int appBoundsCenterY = mAppBounds.centerY();
+ final int appBoundsCenterX = mRelativeAppBounds.centerX();
+ final int appBoundsCenterY = mRelativeAppBounds.centerY();
mTmpTransform.setTranslate(
appBoundsCenterX - mOverlayHalfSize,
appBoundsCenterY - mOverlayHalfSize);
// Scale back the bitmap with the pivot point at center.
final float scale = Math.min(
- (float) mAppBounds.width() / currentBounds.width(),
- (float) mAppBounds.height() / currentBounds.height());
+ (float) mRelativeAppBounds.width() / currentBounds.width(),
+ (float) mRelativeAppBounds.height() / currentBounds.height());
mTmpTransform.postScale(scale, scale, appBoundsCenterX, appBoundsCenterY);
atomicTx.setMatrix(mLeash, mTmpTransform, mTmpFloat9)
.setAlpha(mLeash, fraction < 0.5f ? 0 : (fraction - 0.5f) * 2);
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 ff3dc33d46e8..f296c710f9a0 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
@@ -191,6 +191,16 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
@Override
public void onResult(@Nullable Bundle result) {
mShellExecutor.execute(() -> {
+ if (mBackGestureStarted && result != null && result.getBoolean(
+ BackNavigationInfo.KEY_TOUCH_GESTURE_TRANSFERRED)) {
+ // Host app won't able to process motion event anymore, so pilfer
+ // pointers anyway.
+ if (mBackNavigationInfo != null) {
+ mBackNavigationInfo.disableAppProgressGenerationAllowed();
+ }
+ tryPilferPointers();
+ return;
+ }
if (!mBackGestureStarted || mPostCommitAnimationInProgress) {
// If an uninterruptible animation is already in progress, we should
// ignore this due to it may cause focus lost. (alpha = 0)
@@ -1290,7 +1300,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
final TransitionInfo init = mOpenTransitionInfo;
// Find prepare open target
boolean openShowWallpaper = false;
- final ArrayList<OpenChangeInfo> targets = new ArrayList<>();
+ final ArrayList<SurfaceControl> openSurfaces = new ArrayList<>();
int tmpSize;
for (int j = init.getChanges().size() - 1; j >= 0; --j) {
final TransitionInfo.Change change = init.getChanges().get(j);
@@ -1303,13 +1313,13 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
&& openToken == null) {
continue;
}
- targets.add(new OpenChangeInfo(openComponent, openTaskId, openToken));
+ openSurfaces.add(change.getLeash());
if (change.hasFlags(FLAG_SHOW_WALLPAPER)) {
openShowWallpaper = true;
}
}
}
- if (targets.isEmpty()) {
+ if (openSurfaces.isEmpty()) {
// This shouldn't happen, but if that happen, consume the initial transition anyway.
Log.e(TAG, "Unable to merge following transition, cannot find the gesture "
+ "animated target from the open transition=" + mOpenTransitionInfo);
@@ -1321,7 +1331,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
tmpSize = info.getChanges().size();
for (int j = 0; j < tmpSize; ++j) {
final TransitionInfo.Change change = info.getChanges().get(j);
- if (isOpenChangeMatched(targets, change)) {
+ if (isOpenSurfaceMatched(openSurfaces, change)) {
// This is original close target, potential be close, but cannot determine
// from it.
if (change.hasFlags(FLAG_BACK_GESTURE_ANIMATED)) {
@@ -1342,7 +1352,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
boolean mergePredictive = false;
for (int j = info.getChanges().size() - 1; j >= 0; --j) {
final TransitionInfo.Change change = info.getChanges().get(j);
- if (isOpenChangeMatched(targets, change)) {
+ if (isOpenSurfaceMatched(openSurfaces, change)) {
if (TransitionUtil.isClosingMode(change.getMode())) {
excludeOpenTarget = true;
}
@@ -1363,7 +1373,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
if (change.hasFlags(FLAG_IS_WALLPAPER)) {
continue;
}
- if (isOpenChangeMatched(targets, change)) {
+ if (isOpenSurfaceMatched(openSurfaces, change)) {
if (excludeOpenTarget) {
// App has triggered another change during predictive back
// transition, filter out predictive back target.
@@ -1398,7 +1408,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
if (nonBackClose && nonBackOpen) {
for (int j = info.getChanges().size() - 1; j >= 0; --j) {
final TransitionInfo.Change change = info.getChanges().get(j);
- if (isOpenChangeMatched(targets, change)) {
+ if (isOpenSurfaceMatched(openSurfaces, change)) {
info.getChanges().remove(j);
} else if ((openShowWallpaper && change.hasFlags(FLAG_IS_WALLPAPER))) {
info.getChanges().remove(j);
@@ -1682,22 +1692,10 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
return INVALID_TASK_ID;
}
- private static boolean isSameChangeTarget(ComponentName topActivity, int taskId,
- WindowContainerToken token, TransitionInfo.Change change) {
- final ComponentName openChange = findComponentName(change);
- final int firstTaskId = findTaskId(change);
- final WindowContainerToken openToken = findToken(change);
- return (openChange != null && openChange.equals(topActivity))
- || (firstTaskId != INVALID_TASK_ID && firstTaskId == taskId)
- || (openToken != null && openToken.equals(token));
- }
-
- static boolean isOpenChangeMatched(@NonNull ArrayList<OpenChangeInfo> targets,
+ static boolean isOpenSurfaceMatched(@NonNull ArrayList<SurfaceControl> openSurfaces,
TransitionInfo.Change change) {
- for (int i = targets.size() - 1; i >= 0; --i) {
- final OpenChangeInfo next = targets.get(i);
- if (isSameChangeTarget(next.mOpenComponent, next.mOpenTaskId, next.mOpenToken,
- change)) {
+ for (int i = openSurfaces.size() - 1; i >= 0; --i) {
+ if (openSurfaces.get(i).isSameSurface(change.getLeash())) {
return true;
}
}
@@ -1761,16 +1759,4 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
}
}
}
-
- static class OpenChangeInfo {
- final ComponentName mOpenComponent;
- final int mOpenTaskId;
- final WindowContainerToken mOpenToken;
- OpenChangeInfo(ComponentName openComponent, int openTaskId,
- WindowContainerToken openToken) {
- mOpenComponent = openComponent;
- mOpenTaskId = openTaskId;
- mOpenToken = openToken;
- }
- }
}
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 d1246d35e35e..5f0eed9daa1a 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
@@ -1212,7 +1212,7 @@ public class BubbleController implements ConfigurationChangeListener,
*/
public void startBubbleDrag(String bubbleKey) {
if (mBubbleData.getSelectedBubble() != null) {
- mBubbleBarViewCallback.expansionChanged(/* isExpanded = */ false);
+ collapseExpandedViewForBubbleBar();
}
if (mBubbleStateListener != null) {
boolean overflow = BubbleOverflow.KEY.equals(bubbleKey);
@@ -1304,6 +1304,7 @@ public class BubbleController implements ConfigurationChangeListener,
if (BubbleOverflow.KEY.equals(key)) {
mBubbleData.setSelectedBubbleFromLauncher(mBubbleData.getOverflow());
mLayerView.showExpandedView(mBubbleData.getOverflow());
+ mLogger.log(BubbleLogger.Event.BUBBLE_BAR_EXPANDED);
return;
}
@@ -1315,6 +1316,7 @@ public class BubbleController implements ConfigurationChangeListener,
// already in the stack
mBubbleData.setSelectedBubbleFromLauncher(b);
mLayerView.showExpandedView(b);
+ mLogger.log(b, BubbleLogger.Event.BUBBLE_BAR_EXPANDED);
} else if (mBubbleData.hasOverflowBubbleWithKey(b.getKey())) {
// TODO: (b/271468319) handle overflow
} else {
@@ -2024,12 +2026,16 @@ public class BubbleController implements ConfigurationChangeListener,
public void expansionChanged(boolean isExpanded) {
// in bubble bar mode, let the request to show the expanded view come from launcher.
// only collapse here if we're collapsing.
- if (mLayerView != null && !isExpanded) {
- if (mBubblePositioner.isImeVisible()) {
- // If we're collapsing, hide the IME
- hideCurrentInputMethod();
- }
- mLayerView.collapse();
+ if (!isExpanded) {
+ collapseExpandedViewForBubbleBar();
+ }
+
+ BubbleLogger.Event event = isExpanded ? BubbleLogger.Event.BUBBLE_BAR_EXPANDED
+ : BubbleLogger.Event.BUBBLE_BAR_COLLAPSED;
+ if (mBubbleData.getSelectedBubble() instanceof Bubble bubble) {
+ mLogger.log(bubble, event);
+ } else {
+ mLogger.log(event);
}
}
@@ -2182,6 +2188,16 @@ public class BubbleController implements ConfigurationChangeListener,
}
}
+ private void collapseExpandedViewForBubbleBar() {
+ if (mLayerView != null && mLayerView.isExpanded()) {
+ if (mBubblePositioner.isImeVisible()) {
+ // If we're collapsing, hide the IME
+ hideCurrentInputMethod();
+ }
+ mLayerView.collapse();
+ }
+ }
+
private void updateOverflowButtonDot() {
BubbleOverflow overflow = mBubbleData.getOverflow();
if (overflow == null) return;
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 35a0d07a63b2..88f55b8af8f7 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
@@ -1704,6 +1704,7 @@ public class BubbleStackView extends FrameLayout
getViewTreeObserver().removeOnPreDrawListener(mViewUpdater);
getViewTreeObserver().removeOnDrawListener(mSystemGestureExcludeUpdater);
getViewTreeObserver().removeOnComputeInternalInsetsListener(this);
+ stopMonitoringSwipeUpGesture();
}
@Override
@@ -2313,7 +2314,8 @@ public class BubbleStackView extends FrameLayout
/**
* Stop monitoring for swipe up gesture
*/
- void stopMonitoringSwipeUpGesture() {
+ @VisibleForTesting
+ public void stopMonitoringSwipeUpGesture() {
stopMonitoringSwipeUpGestureInternal();
}
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 b29e49a48428..813772f20a8a 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
@@ -71,6 +71,11 @@ public class DividerSnapAlgorithm {
*/
private static final int SNAP_MODE_MINIMIZED = 3;
+ /**
+ * A mode where apps can be "flexibly offscreen" on smaller displays.
+ */
+ private static final int SNAP_FLEXIBLE_SPLIT = 4;
+
private final float mMinFlingVelocityPxPerSecond;
private final float mMinDismissVelocityPxPerSecond;
private final int mDisplayWidth;
@@ -78,6 +83,7 @@ public class DividerSnapAlgorithm {
private final int mDividerSize;
private final ArrayList<SnapTarget> mTargets = new ArrayList<>();
private final Rect mInsets = new Rect();
+ private final Rect mPinnedTaskbarInsets = new Rect();
private final int mSnapMode;
private final boolean mFreeSnapMode;
private final int mMinimalSizeResizableTask;
@@ -88,6 +94,8 @@ public class DividerSnapAlgorithm {
/** Allows split ratios that go offscreen (a.k.a. "flexible split") */
private final boolean mAllowOffscreenRatios;
private final boolean mIsLeftRightSplit;
+ /** In SNAP_MODE_MINIMIZED, the side of the screen on which an app will "dock" when minimized */
+ private final int mDockSide;
/** The first target which is still splitting the screen */
private final SnapTarget mFirstSplitTarget;
@@ -101,14 +109,14 @@ public class DividerSnapAlgorithm {
public DividerSnapAlgorithm(Resources res, int displayWidth, int displayHeight, int dividerSize,
- boolean isLeftRightSplit, Rect insets, int dockSide) {
+ boolean isLeftRightSplit, Rect insets, Rect pinnedTaskbarInsets, int dockSide) {
this(res, displayWidth, displayHeight, dividerSize, isLeftRightSplit, insets,
- dockSide, false /* minimized */, true /* resizable */);
+ pinnedTaskbarInsets, dockSide, false /* minimized */, true /* resizable */);
}
public DividerSnapAlgorithm(Resources res, int displayWidth, int displayHeight, int dividerSize,
- boolean isLeftRightSplit, Rect insets, int dockSide, boolean isMinimizedMode,
- boolean isHomeResizable) {
+ boolean isLeftRightSplit, Rect insets, Rect pinnedTaskbarInsets, int dockSide,
+ boolean isMinimizedMode, boolean isHomeResizable) {
mMinFlingVelocityPxPerSecond =
MIN_FLING_VELOCITY_DP_PER_SECOND * res.getDisplayMetrics().density;
mMinDismissVelocityPxPerSecond =
@@ -117,10 +125,11 @@ public class DividerSnapAlgorithm {
mDisplayWidth = displayWidth;
mDisplayHeight = displayHeight;
mIsLeftRightSplit = isLeftRightSplit;
+ mDockSide = dockSide;
mInsets.set(insets);
+ mPinnedTaskbarInsets.set(pinnedTaskbarInsets);
if (Flags.enableFlexibleTwoAppSplit()) {
- // In flexible split, we always use a fixed ratio (50%, 66%, or 90%) for splitting
- mSnapMode = SNAP_FIXED_RATIO;
+ mSnapMode = SNAP_FLEXIBLE_SPLIT;
} else {
// Set SNAP_MODE_MINIMIZED, SNAP_MODE_16_9, or SNAP_FIXED_RATIO depending on config
mSnapMode = isMinimizedMode
@@ -140,7 +149,7 @@ public class DividerSnapAlgorithm {
mAllowOffscreenRatios = SplitScreenUtils.allowOffscreenRatios(res);
mTaskHeightInMinimizedMode = isHomeResizable ? res.getDimensionPixelSize(
com.android.internal.R.dimen.task_height_of_minimized_mode) : 0;
- calculateTargets(isLeftRightSplit, dockSide);
+ calculateTargets();
mFirstSplitTarget = mTargets.get(1);
mLastSplitTarget = mTargets.get(mTargets.size() - 2);
mDismissStartTarget = mTargets.get(0);
@@ -276,28 +285,31 @@ public class DividerSnapAlgorithm {
return mTargets.get(minIndex);
}
- private void calculateTargets(boolean isLeftRightSplit, int dockedSide) {
+ private void calculateTargets() {
mTargets.clear();
- int dividerMax = isLeftRightSplit
+ int dividerMax = mIsLeftRightSplit
? mDisplayWidth
: mDisplayHeight;
int startPos = -mDividerSize;
- if (dockedSide == DOCKED_RIGHT) {
+ if (mDockSide == DOCKED_RIGHT) {
startPos += mInsets.left;
}
mTargets.add(new SnapTarget(startPos, SNAP_TO_START_AND_DISMISS, 0.35f));
switch (mSnapMode) {
case SNAP_MODE_16_9:
- addRatio16_9Targets(isLeftRightSplit, dividerMax);
+ addRatio16_9Targets(mIsLeftRightSplit, dividerMax);
break;
case SNAP_FIXED_RATIO:
- addFixedDivisionTargets(isLeftRightSplit, dividerMax);
+ addFixedDivisionTargets(mIsLeftRightSplit, dividerMax);
break;
case SNAP_ONLY_1_1:
- addMiddleTarget(isLeftRightSplit);
+ addMiddleTarget(mIsLeftRightSplit);
break;
case SNAP_MODE_MINIMIZED:
- addMinimizedTarget(isLeftRightSplit, dockedSide);
+ addMinimizedTarget(mIsLeftRightSplit, mDockSide);
+ break;
+ case SNAP_FLEXIBLE_SPLIT:
+ addFlexSplitTargets(mIsLeftRightSplit, dividerMax);
break;
}
mTargets.add(new SnapTarget(dividerMax, SNAP_TO_END_AND_DISMISS, 0.35f));
@@ -321,18 +333,9 @@ public class DividerSnapAlgorithm {
? mDisplayWidth - mInsets.right
: mDisplayHeight - mInsets.bottom;
- int size;
- if (Flags.enableFlexibleTwoAppSplit()) {
- float ratio = areOffscreenRatiosSupported()
- ? SplitLayout.OFFSCREEN_ASYMMETRIC_RATIO
- : SplitLayout.ONSCREEN_ONLY_ASYMMETRIC_RATIO;
- size = (int) (ratio * (end - start)) - mDividerSize / 2;
- } else {
- size = (int) (mFixedRatio * (end - start)) - mDividerSize / 2;
-
- if (mCalculateRatiosBasedOnAvailableSpace) {
- size = Math.max(size, mMinimalSizeResizableTask);
- }
+ int size = (int) (mFixedRatio * (end - start)) - mDividerSize / 2;
+ if (mCalculateRatiosBasedOnAvailableSpace) {
+ size = Math.max(size, mMinimalSizeResizableTask);
}
int topPosition = start + size;
@@ -340,6 +343,24 @@ public class DividerSnapAlgorithm {
addNonDismissingTargets(isLeftRightSplit, topPosition, bottomPosition, dividerMax);
}
+ private void addFlexSplitTargets(boolean isLeftRightSplit, int dividerMax) {
+ int start = 0;
+ int end = isLeftRightSplit ? mDisplayWidth : mDisplayHeight;
+ int pinnedTaskbarShiftStart = isLeftRightSplit
+ ? mPinnedTaskbarInsets.left : mPinnedTaskbarInsets.top;
+ int pinnedTaskbarShiftEnd = isLeftRightSplit
+ ? mPinnedTaskbarInsets.right : mPinnedTaskbarInsets.bottom;
+
+ float ratio = areOffscreenRatiosSupported()
+ ? SplitLayout.OFFSCREEN_ASYMMETRIC_RATIO
+ : SplitLayout.ONSCREEN_ONLY_ASYMMETRIC_RATIO;
+ int size = (int) (ratio * (end - start)) - mDividerSize / 2;
+
+ int leftTopPosition = start + pinnedTaskbarShiftStart + size;
+ int rightBottomPosition = end - pinnedTaskbarShiftEnd - size - mDividerSize;
+ addNonDismissingTargets(isLeftRightSplit, leftTopPosition, rightBottomPosition, dividerMax);
+ }
+
private void addRatio16_9Targets(boolean isLeftRightSplit, int dividerMax) {
int start = isLeftRightSplit ? mInsets.left : mInsets.top;
int end = isLeftRightSplit
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 b1e0e9eab5d0..dab30b0f0b96 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
@@ -49,10 +49,13 @@ import android.app.ActivityManager;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
+import android.graphics.Insets;
import android.graphics.Point;
import android.graphics.Rect;
import android.os.Handler;
import android.view.Display;
+import android.view.InsetsController;
+import android.view.InsetsSource;
import android.view.InsetsSourceControl;
import android.view.InsetsState;
import android.view.RoundedCorner;
@@ -153,6 +156,7 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
private final ResizingEffectPolicy mSurfaceEffectPolicy;
private final ShellTaskOrganizer mTaskOrganizer;
private final InsetsState mInsetsState = new InsetsState();
+ private Insets mPinnedTaskbarInsets = Insets.NONE;
private Context mContext;
@VisibleForTesting DividerSnapAlgorithm mDividerSnapAlgorithm;
@@ -523,6 +527,7 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
@Override
public void insetsChanged(InsetsState insetsState) {
mInsetsState.set(insetsState);
+
if (!mInitialized) {
return;
}
@@ -531,9 +536,51 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
// flicker.
return;
}
+
+ // Check to see if insets changed in such a way that the divider algorithm needs to be
+ // recalculated.
+ Insets pinnedTaskbarInsets = calculatePinnedTaskbarInsets(insetsState);
+ if (!mPinnedTaskbarInsets.equals(pinnedTaskbarInsets)) {
+ mPinnedTaskbarInsets = pinnedTaskbarInsets;
+ // Refresh the DividerSnapAlgorithm.
+ mDividerSnapAlgorithm = getSnapAlgorithm(mContext, mRootBounds);
+ // 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 */);
+ if (snapTarget.position != mDividerPosition) {
+ snapToTarget(mDividerPosition, snapTarget,
+ InsetsController.ANIMATION_DURATION_RESIZE,
+ InsetsController.RESIZE_INTERPOLATOR);
+ }
+ }
+
mSplitWindowManager.onInsetsChanged(insetsState);
}
+ /**
+ * Calculates the insets that might trigger a divider algorithm recalculation. Currently, only
+ * pinned Taskbar does this, and only when the IME is not showing.
+ */
+ private Insets calculatePinnedTaskbarInsets(InsetsState insetsState) {
+ if (insetsState.isSourceOrDefaultVisible(InsetsSource.ID_IME, WindowInsets.Type.ime())) {
+ return Insets.NONE;
+ }
+
+ // If IME is not showing...
+ for (int i = insetsState.sourceSize() - 1; i >= 0; i--) {
+ final InsetsSource source = insetsState.sourceAt(i);
+ // and Taskbar is pinned...
+ if (source.getType() == WindowInsets.Type.navigationBars()
+ && source.hasFlags(InsetsSource.FLAG_INSETS_ROUNDED_CORNER)) {
+ // Return Insets representing the pinned taskbar state.
+ return source.calculateVisibleInsets(mRootBounds);
+ }
+ }
+
+ // Else, divider can calculate based on the full display.
+ return Insets.NONE;
+ }
+
@Override
public void insetsControlChanged(InsetsState insetsState,
InsetsSourceControl[] activeControls) {
@@ -631,8 +678,8 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
}
/**
- * Same as {@link #snapToTarget(int, SnapTarget)}, with default animation duration and
- * interpolator.
+ * Same as {@link #snapToTarget(int, SnapTarget, int, Interpolator)}, with default animation
+ * duration and interpolator.
*/
public void snapToTarget(int currentPosition, SnapTarget snapTarget) {
snapToTarget(currentPosition, snapTarget, FLING_RESIZE_DURATION,
@@ -683,6 +730,7 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
mDividerSize,
mIsLeftRightSplit,
insets,
+ mPinnedTaskbarInsets.toRect(),
mIsLeftRightSplit ? DOCKED_LEFT : DOCKED_TOP /* dockSide */);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUILayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUILayout.java
index 688f8ca2dc75..49c2785f1ecd 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUILayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUILayout.java
@@ -68,7 +68,7 @@ class CompatUILayout extends LinearLayout {
private void setViewVisibility(@IdRes int resId, boolean show) {
final View view = findViewById(resId);
- int visibility = show ? View.VISIBLE : View.GONE;
+ int visibility = show ? View.VISIBLE : View.INVISIBLE;
if (view.getVisibility() == visibility) {
return;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/UserAspectRatioSettingsLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/UserAspectRatioSettingsLayout.java
index b141bebbe8b1..fd1bbc477cf4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/UserAspectRatioSettingsLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/UserAspectRatioSettingsLayout.java
@@ -100,7 +100,7 @@ public class UserAspectRatioSettingsLayout extends LinearLayout {
private void setViewVisibility(@IdRes int resId, boolean show) {
final View view = findViewById(resId);
- int visibility = show ? View.VISIBLE : View.GONE;
+ int visibility = show ? View.VISIBLE : View.INVISIBLE;
if (view.getVisibility() == visibility) {
return;
}
@@ -171,7 +171,7 @@ public class UserAspectRatioSettingsLayout extends LinearLayout {
fadeOut.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
- view.setVisibility(View.GONE);
+ view.setVisibility(View.INVISIBLE);
}
});
fadeOut.start();
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 615dad31534f..817be3b1fe0d 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
@@ -970,7 +970,10 @@ public abstract class WMShellModule {
CloseDesktopTaskTransitionHandler closeDesktopTaskTransitionHandler,
Optional<DesktopImmersiveController> desktopImmersiveController,
InteractionJankMonitor interactionJankMonitor,
- @ShellMainThread Handler handler) {
+ @ShellMainThread Handler handler,
+ ShellInit shellInit,
+ RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer
+ ) {
if (!DesktopModeStatus.canEnterDesktopMode(context)) {
return Optional.empty();
}
@@ -983,7 +986,9 @@ public abstract class WMShellModule {
closeDesktopTaskTransitionHandler,
desktopImmersiveController.get(),
interactionJankMonitor,
- handler));
+ handler,
+ shellInit,
+ rootTaskDisplayAreaOrganizer));
}
@WMSingleton
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 e741892bf6fc..f69aa6df6a1d 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
@@ -224,9 +224,9 @@ class DesktopImmersiveController(
finishTransaction: SurfaceControl.Transaction,
finishCallback: Transitions.TransitionFinishCallback
): Boolean {
- logD("startAnimation transition=%s", transition)
val state = requireState()
if (transition != state.transition) return false
+ logD("startAnimation transition=%s", transition)
animateResize(
targetTaskId = state.taskId,
info = info,
@@ -334,7 +334,6 @@ class DesktopImmersiveController(
startTransaction: SurfaceControl.Transaction,
finishTransaction: SurfaceControl.Transaction,
) {
- logD("onTransitionReady transition=%s", transition)
// Check if this is a pending external exit transition.
val pendingExit = pendingExternalExitTransitions
.firstOrNull { pendingExit -> pendingExit.transition == transition }
@@ -402,7 +401,6 @@ class DesktopImmersiveController(
}
override fun onTransitionMerged(merged: IBinder, playing: IBinder) {
- logD("onTransitionMerged merged=%s playing=%s", merged, playing)
val pendingExit = pendingExternalExitTransitions
.firstOrNull { pendingExit -> pendingExit.transition == merged }
if (pendingExit != null) {
@@ -415,7 +413,6 @@ class DesktopImmersiveController(
}
override fun onTransitionFinished(transition: IBinder, aborted: Boolean) {
- logD("onTransitionFinished transition=%s aborted=%b", transition, aborted)
val pendingExit = pendingExternalExitTransitions
.firstOrNull { pendingExit -> pendingExit.transition == transition }
if (pendingExit != null) {
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 0bc571f4782c..48bb2a8b4a74 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
@@ -25,6 +25,7 @@ import android.view.SurfaceControl
import android.view.WindowManager
import android.window.DesktopModeFlags
import android.window.TransitionInfo
+import android.window.TransitionInfo.Change
import android.window.TransitionRequestInfo
import android.window.WindowContainerTransaction
import androidx.annotation.VisibleForTesting
@@ -32,10 +33,13 @@ import com.android.internal.jank.Cuj.CUJ_DESKTOP_MODE_EXIT_MODE_ON_LAST_WINDOW_C
import com.android.internal.jank.InteractionJankMonitor
import com.android.internal.protolog.ProtoLog
import com.android.window.flags.Flags
+import com.android.wm.shell.RootTaskDisplayAreaOrganizer
import com.android.wm.shell.freeform.FreeformTaskTransitionHandler
import com.android.wm.shell.freeform.FreeformTaskTransitionStarter
import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE
+import com.android.wm.shell.shared.TransitionUtil
import com.android.wm.shell.shared.annotations.ShellMainThread
+import com.android.wm.shell.sysui.ShellInit
import com.android.wm.shell.transition.MixedTransitionHandler
import com.android.wm.shell.transition.Transitions
import com.android.wm.shell.transition.Transitions.TransitionFinishCallback
@@ -50,8 +54,14 @@ class DesktopMixedTransitionHandler(
private val desktopImmersiveController: DesktopImmersiveController,
private val interactionJankMonitor: InteractionJankMonitor,
@ShellMainThread private val handler: Handler,
+ shellInit: ShellInit,
+ private val rootTaskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer,
) : MixedTransitionHandler, FreeformTaskTransitionStarter {
+ init {
+ shellInit.addInitCallback ({ transitions.addHandler(this) }, this)
+ }
+
@VisibleForTesting
val pendingMixedTransitions = mutableListOf<PendingMixedTransition>()
@@ -85,9 +95,11 @@ class DesktopMixedTransitionHandler(
@WindowManager.TransitionType transitionType: Int,
wct: WindowContainerTransaction,
taskId: Int,
+ minimizingTaskId: Int? = null,
exitingImmersiveTask: Int? = null,
): IBinder {
- if (!Flags.enableFullyImmersiveInDesktop()) {
+ if (!Flags.enableFullyImmersiveInDesktop() &&
+ !DesktopModeFlags.ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS.isTrue) {
return transitions.startTransition(transitionType, wct, /* handler= */ null)
}
if (exitingImmersiveTask == null) {
@@ -103,11 +115,17 @@ class DesktopMixedTransitionHandler(
pendingMixedTransitions.add(PendingMixedTransition.Launch(
transition = transition,
launchingTask = taskId,
- exitingImmersiveTask = exitingImmersiveTask
+ minimizingTask = minimizingTaskId,
+ exitingImmersiveTask = exitingImmersiveTask,
))
}
}
+ /** Notifies this handler that there is a pending transition for it to handle. */
+ fun addPendingMixedTransition(pendingMixedTransition: PendingMixedTransition) {
+ pendingMixedTransitions.add(pendingMixedTransition)
+ }
+
/** Returns null, as it only handles transitions started from Shell. */
override fun handleRequest(
transition: IBinder,
@@ -123,7 +141,7 @@ class DesktopMixedTransitionHandler(
): Boolean {
val pending = pendingMixedTransitions.find { pending -> pending.transition == transition }
?: return false.also {
- logW("Should have pending desktop transition")
+ logV("No pending desktop transition")
}
pendingMixedTransitions.remove(pending)
logV("Animating pending mixed transition: %s", pending)
@@ -191,6 +209,9 @@ class DesktopMixedTransitionHandler(
val immersiveExitChange = pending.exitingImmersiveTask?.let { exitingTask ->
findDesktopTaskChange(info, exitingTask)
}
+ val minimizeChange = pending.minimizingTask?.let { minimizingTask ->
+ findDesktopTaskChange(info, minimizingTask)
+ }
val launchChange = findDesktopTaskChange(info, pending.launchingTask)
?: error("Should have pending launching task change")
@@ -204,9 +225,17 @@ class DesktopMixedTransitionHandler(
}
logV(
- "Animating pending mixed launch transition task#%d immersiveExitTask#%s",
- launchChange.taskInfo!!.taskId, immersiveExitChange?.taskInfo?.taskId
+ "Animating mixed launch transition task#%d, minimizingTask#%s immersiveExitTask#%s",
+ launchChange.taskInfo!!.taskId, minimizeChange?.taskInfo?.taskId,
+ immersiveExitChange?.taskInfo?.taskId
)
+ if (DesktopModeFlags.ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS.isTrue) {
+ // Only apply minimize change reparenting here if we implement the new app launch
+ // transitions, otherwise this reparenting is handled in the default handler.
+ minimizeChange?.let {
+ applyMinimizeChangeReparenting(info, minimizeChange, startTransaction)
+ }
+ }
if (immersiveExitChange != null) {
subAnimationCount = 2
// Animate the immersive exit change separately.
@@ -257,7 +286,7 @@ class DesktopMixedTransitionHandler(
change: TransitionInfo.Change,
startTransaction: SurfaceControl.Transaction,
finishTransaction: SurfaceControl.Transaction,
- finishCallback: Transitions.TransitionFinishCallback,
+ finishCallback: TransitionFinishCallback,
): Boolean {
// Starting the jank trace if closing the last window in desktop mode.
interactionJankMonitor.begin(
@@ -280,6 +309,28 @@ class DesktopMixedTransitionHandler(
)
}
+ /**
+ * Reparent the minimizing task back to its root display area.
+ *
+ * During the launch/minimize animation the all animated tasks will be reparented to a
+ * transition leash shown in front of other desktop tasks. Reparenting the minimizing task back
+ * to its root display area ensures that task stays behind other desktop tasks during the
+ * animation.
+ */
+ private fun applyMinimizeChangeReparenting(
+ info: TransitionInfo,
+ minimizeChange: Change,
+ startTransaction: SurfaceControl.Transaction,
+ ) {
+ require(TransitionUtil.isOpeningMode(info.type))
+ require(minimizeChange.taskInfo != null)
+ val taskInfo = minimizeChange.taskInfo!!
+ require(taskInfo.isFreeform)
+ logV("Reparenting minimizing task#%d", taskInfo.taskId)
+ rootTaskDisplayAreaOrganizer.reparentToDisplayArea(
+ taskInfo.displayId, minimizeChange.leash, startTransaction)
+ }
+
private fun dispatchToLeftoverHandler(
transition: IBinder,
info: TransitionInfo,
@@ -341,6 +392,7 @@ class DesktopMixedTransitionHandler(
data class Launch(
override val transition: IBinder,
val launchingTask: Int,
+ val minimizingTask: Int?,
val exitingImmersiveTask: Int?,
) : PendingMixedTransition()
}
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 5648feb96b06..fda709a4a2d7 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
@@ -23,12 +23,12 @@ import android.util.ArrayMap
import android.util.ArraySet
import android.util.SparseArray
import android.view.Display.INVALID_DISPLAY
+import android.window.DesktopModeFlags
import android.window.WindowContainerToken
import androidx.core.util.forEach
import androidx.core.util.keyIterator
import androidx.core.util.valueIterator
import com.android.internal.protolog.ProtoLog
-import com.android.window.flags.Flags
import com.android.wm.shell.desktopmode.persistence.DesktopPersistentRepository
import com.android.wm.shell.desktopmode.persistence.DesktopRepositoryInitializer
import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE
@@ -294,7 +294,7 @@ class DesktopRepository (
taskId, isVisible, displayId)
logD("VisibleTaskCount has changed from %d to %d", prevCount, newCount)
notifyVisibleTaskListeners(displayId, newCount)
- if (Flags.enableDesktopWindowingPersistence()) {
+ if (DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_PERSISTENCE.isTrue()) {
updatePersistentRepository(displayId)
}
}
@@ -344,7 +344,7 @@ class DesktopRepository (
desktopTaskDataByDisplayId.getOrCreate(displayId).freeformTasksInZOrder.add(0, taskId)
// Unminimize the task if it is minimized.
unminimizeTask(displayId, taskId)
- if (Flags.enableDesktopWindowingPersistence()) {
+ if (DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_PERSISTENCE.isTrue()) {
updatePersistentRepository(displayId)
}
}
@@ -362,7 +362,7 @@ class DesktopRepository (
desktopTaskDataByDisplayId.getOrCreate(displayId).minimizedTasks.add(taskId)
}
updateTask(displayId, taskId, isVisible = false)
- if (Flags.enableDesktopWindowingPersistence()) {
+ if (DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_PERSISTENCE.isTrue()) {
updatePersistentRepository(displayId)
}
}
@@ -410,7 +410,7 @@ class DesktopRepository (
unminimizeTask(displayId, taskId)
removeActiveTask(taskId)
removeVisibleTask(taskId)
- if (Flags.enableDesktopWindowingPersistence()) {
+ if (DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_PERSISTENCE.isTrue()) {
updatePersistentRepository(displayId)
}
}
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 133f0693b227..75f8839692a2 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
@@ -698,6 +698,7 @@ class DesktopTasksController(
transitionType = transitionType,
wct = wct,
taskId = taskId,
+ minimizingTaskId = taskIdToMinimize,
exitingImmersiveTask = exitingImmersiveTask,
)
taskIdToMinimize?.let { addPendingMinimizeTransition(t, it) }
@@ -1161,7 +1162,7 @@ class DesktopTasksController(
if (runningTaskInfo != null) {
// Task is already running, reorder it to the front
wct.reorder(runningTaskInfo.token, /* onTop= */ true)
- } else if (Flags.enableDesktopWindowingPersistence()) {
+ } else if (DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_PERSISTENCE.isTrue()) {
// Task is not running, start it
wct.startTask(
taskId,
@@ -1434,6 +1435,7 @@ class DesktopTasksController(
)
val transition = transitions.startTransition(TRANSIT_OPEN, wct, null)
taskIdToMinimize?.let { addPendingMinimizeTransition(transition, it) }
+ addPendingAppLaunchTransition(transition, requestedTaskId, taskIdToMinimize)
exitResult.asExit()?.runOnTransitionStart?.invoke(transition)
} else {
val splitPosition = splitScreenController.determineNewInstancePosition(callingTask)
@@ -1574,6 +1576,7 @@ class DesktopTasksController(
desktopImmersiveController.exitImmersiveIfApplicable(transition, wct, task.displayId)
// 2) minimize a Task if needed.
val taskIdToMinimize = addAndGetMinimizeChanges(task.displayId, wct, task.taskId)
+ addPendingAppLaunchTransition(transition, task.taskId, taskIdToMinimize)
if (taskIdToMinimize != null) {
addPendingMinimizeTransition(transition, taskIdToMinimize)
return wct
@@ -1605,6 +1608,7 @@ class DesktopTasksController(
// minimize another Task.
val taskIdToMinimize = addAndGetMinimizeChanges(task.displayId, wct, task.taskId)
taskIdToMinimize?.let { addPendingMinimizeTransition(transition, it) }
+ addPendingAppLaunchTransition(transition, task.taskId, taskIdToMinimize)
desktopImmersiveController.exitImmersiveIfApplicable(
transition, wct, task.displayId
)
@@ -1785,6 +1789,20 @@ class DesktopTasksController(
}
}
+ private fun addPendingAppLaunchTransition(
+ transition: IBinder,
+ launchTaskId: Int,
+ minimizeTaskId: Int?,
+ ) {
+ if (!DesktopModeFlags.ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS.isTrue) {
+ return
+ }
+ // TODO b/359523924: pass immersive task here?
+ desktopMixedTransitionHandler.addPendingMixedTransition(
+ DesktopMixedTransitionHandler.PendingMixedTransition.Launch(
+ transition, launchTaskId, minimizeTaskId, /* exitingImmersiveTask= */ null))
+ }
+
fun removeDesktop(displayId: Int) {
if (!DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION.isTrue()) return
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ExitDesktopTaskTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ExitDesktopTaskTransitionHandler.java
index dedd44f3950a..d537da802efd 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ExitDesktopTaskTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ExitDesktopTaskTransitionHandler.java
@@ -60,7 +60,8 @@ import java.util.function.Supplier;
* entering and exiting freeform.
*/
public class ExitDesktopTaskTransitionHandler implements Transitions.TransitionHandler {
- private static final int FULLSCREEN_ANIMATION_DURATION = 336;
+ @VisibleForTesting
+ static final int FULLSCREEN_ANIMATION_DURATION = 336;
private final Context mContext;
private final Transitions mTransitions;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/persistence/DesktopRepositoryInitializerImpl.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/persistence/DesktopRepositoryInitializerImpl.kt
index fc4ed15157d6..d8156561ff19 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/persistence/DesktopRepositoryInitializerImpl.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/persistence/DesktopRepositoryInitializerImpl.kt
@@ -17,7 +17,7 @@
package com.android.wm.shell.desktopmode.persistence
import android.content.Context
-import com.android.window.flags.Flags
+import android.window.DesktopModeFlags
import com.android.wm.shell.desktopmode.DesktopRepository
import com.android.wm.shell.shared.annotations.ShellMainThread
import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
@@ -36,7 +36,7 @@ class DesktopRepositoryInitializerImpl(
@ShellMainThread private val mainCoroutineScope: CoroutineScope,
) : DesktopRepositoryInitializer {
override fun initialize(repository: DesktopRepository) {
- if (!Flags.enableDesktopWindowingPersistence()) return
+ if (!DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_PERSISTENCE.isTrue()) return
// TODO: b/365962554 - Handle the case that user moves to desktop before it's initialized
mainCoroutineScope.launch {
val desktop = persistentRepository.readDesktop() ?: return@launch
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
index f060158766fe..4aeecbec7dfb 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
@@ -30,6 +30,7 @@ import android.annotation.NonNull;
import android.app.TaskInfo;
import android.content.Context;
import android.content.pm.ActivityInfo;
+import android.graphics.Point;
import android.graphics.Rect;
import android.os.SystemClock;
import android.view.Surface;
@@ -152,7 +153,6 @@ public class PipAnimationController {
return mCurrentAnimator;
}
- @SuppressWarnings("unchecked")
/**
* Construct and return an animator that animates from the {@param startBounds} to the
* {@param endBounds} with the given {@param direction}. If {@param direction} is type
@@ -171,6 +171,7 @@ public class PipAnimationController {
* leaving PiP to fullscreen, and the {@param endBounds} is the fullscreen bounds before the
* rotation change.
*/
+ @SuppressWarnings("unchecked")
@VisibleForTesting
public PipTransitionAnimator getAnimator(TaskInfo taskInfo, SurfaceControl leash,
Rect baseBounds, Rect startBounds, Rect endBounds, Rect sourceHintRect,
@@ -566,7 +567,7 @@ public class PipAnimationController {
}
getSurfaceTransactionHelper()
.resetScale(tx, leash, getDestinationBounds())
- .crop(tx, leash, getDestinationBounds())
+ .cropAndPosition(tx, leash, getDestinationBounds())
.round(tx, leash, true /* applyCornerRadius */)
.shadow(tx, leash, shouldApplyShadowRadius());
tx.show(leash);
@@ -590,18 +591,50 @@ public class PipAnimationController {
// Just for simplicity we'll interpolate between the source rect hint insets and empty
// insets to calculate the window crop
final Rect initialSourceValue;
+ final Rect mainWindowFrame = taskInfo.topActivityMainWindowFrame;
+ final boolean hasNonMatchFrame = mainWindowFrame != null;
+ final boolean changeOrientation =
+ rotationDelta == ROTATION_90 || rotationDelta == ROTATION_270;
+ final Rect baseBounds = new Rect(baseValue);
+ final Rect startBounds = new Rect(startValue);
+ final Rect endBounds = new Rect(endValue);
if (isOutPipDirection) {
- initialSourceValue = new Rect(endValue);
+ // TODO(b/356277166): handle rotation change with activity that provides main window
+ // frame.
+ if (hasNonMatchFrame && !changeOrientation) {
+ endBounds.set(mainWindowFrame);
+ }
+ initialSourceValue = new Rect(endBounds);
+ } else if (isInPipDirection) {
+ if (hasNonMatchFrame) {
+ baseBounds.set(mainWindowFrame);
+ if (startValue.equals(baseValue)) {
+ // If the start value is at initial state as in PIP animation, also override
+ // the start bounds with nonMatchParentBounds.
+ startBounds.set(mainWindowFrame);
+ }
+ }
+ initialSourceValue = new Rect(baseBounds);
} else {
- initialSourceValue = new Rect(baseValue);
+ // Note that we assume the window bounds always match task bounds in PIP mode.
+ initialSourceValue = new Rect(baseBounds);
+ }
+
+ final Point leashOffset;
+ if (isInPipDirection) {
+ leashOffset = new Point(baseValue.left, baseValue.top);
+ } else if (isOutPipDirection) {
+ leashOffset = new Point(endValue.left, endValue.top);
+ } else {
+ leashOffset = new Point(baseValue.left, baseValue.top);
}
final Rect rotatedEndRect;
final Rect lastEndRect;
final Rect initialContainerRect;
- if (rotationDelta == ROTATION_90 || rotationDelta == ROTATION_270) {
- lastEndRect = new Rect(endValue);
- rotatedEndRect = new Rect(endValue);
+ if (changeOrientation) {
+ lastEndRect = new Rect(endBounds);
+ rotatedEndRect = new Rect(endBounds);
// Rotate the end bounds according to the rotation delta because the display will
// be rotated to the same orientation.
rotateBounds(rotatedEndRect, initialSourceValue, rotationDelta);
@@ -617,9 +650,9 @@ public class PipAnimationController {
// Crop a Rect matches the aspect ratio and pivots at the center point.
// This is done for entering case only.
if (isInPipDirection(direction)) {
- final float aspectRatio = endValue.width() / (float) endValue.height();
+ final float aspectRatio = endBounds.width() / (float) endBounds.height();
adjustedSourceRectHint.set(PipUtils.getEnterPipWithOverlaySrcRectHint(
- startValue, aspectRatio));
+ startBounds, aspectRatio));
}
} else {
adjustedSourceRectHint.set(sourceRectHint);
@@ -644,7 +677,7 @@ public class PipAnimationController {
// construct new Rect instances in case they are recycled
return new PipTransitionAnimator<Rect>(taskInfo, leash, ANIM_TYPE_BOUNDS,
- endValue, new Rect(baseValue), new Rect(startValue), new Rect(endValue)) {
+ endBounds, new Rect(baseBounds), new Rect(startBounds), new Rect(endBounds)) {
private final RectEvaluator mRectEvaluator = new RectEvaluator(new Rect());
private final RectEvaluator mInsetsEvaluator = new RectEvaluator(new Rect());
@@ -668,11 +701,22 @@ public class PipAnimationController {
setCurrentValue(bounds);
if (inScaleTransition() || adjustedSourceRectHint.isEmpty()) {
if (isOutPipDirection) {
- getSurfaceTransactionHelper().crop(tx, leash, end)
- .scale(tx, leash, end, bounds);
+ // Use the bounds relative to the task leash in case the leash does not
+ // start from (0, 0).
+ final Rect relativeEndBounds = new Rect(end);
+ relativeEndBounds.offset(-leashOffset.x, -leashOffset.y);
+ getSurfaceTransactionHelper()
+ .crop(tx, leash, relativeEndBounds)
+ .scale(tx, leash, relativeEndBounds, bounds,
+ false /* shouldOffset */);
} else {
- getSurfaceTransactionHelper().crop(tx, leash, base)
- .scale(tx, leash, base, bounds, angle)
+ // TODO(b/356277166): add support to specify sourceRectHint with
+ // non-match parent activity.
+ // If there's a PIP resize animation, we should offset the bounds to
+ // (0, 0) since the window bounds should match the leash bounds in PIP
+ // mode.
+ getSurfaceTransactionHelper().cropAndPosition(tx, leash, base)
+ .scale(tx, leash, base, bounds, angle, inScaleTransition())
.round(tx, leash, base, bounds)
.shadow(tx, leash, shouldApplyShadowRadius());
}
@@ -680,7 +724,7 @@ public class PipAnimationController {
final Rect insets = computeInsets(fraction);
getSurfaceTransactionHelper().scaleAndCrop(tx, leash,
adjustedSourceRectHint, initialSourceValue, bounds, insets,
- isInPipDirection, fraction);
+ isInPipDirection, fraction, leashOffset);
final Rect sourceBounds = new Rect(initialContainerRect);
sourceBounds.inset(insets);
getSurfaceTransactionHelper()
@@ -733,8 +777,7 @@ public class PipAnimationController {
getSurfaceTransactionHelper()
.rotateAndScaleWithCrop(tx, leash, initialContainerRect, bounds,
insets, degree, x, y, isOutPipDirection,
- rotationDelta == ROTATION_270 /* clockwise */);
- getSurfaceTransactionHelper()
+ rotationDelta == ROTATION_270 /* clockwise */)
.round(tx, leash, sourceBounds, bounds)
.shadow(tx, leash, shouldApplyShadowRadius());
if (!handlePipTransaction(leash, tx, bounds, 1f /* alpha */)) {
@@ -772,7 +815,7 @@ public class PipAnimationController {
tx.setPosition(leash, 0, 0);
tx.setWindowCrop(leash, 0, 0);
} else {
- getSurfaceTransactionHelper().crop(tx, leash, destBounds);
+ getSurfaceTransactionHelper().cropAndPosition(tx, leash, destBounds);
}
if (mContentOverlay != null) {
clearContentOverlay();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java
index 3d1994cac534..b02bd0ffec6c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java
@@ -16,8 +16,10 @@
package com.android.wm.shell.pip;
+import android.annotation.NonNull;
import android.content.Context;
import android.graphics.Matrix;
+import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.RectF;
import android.view.Choreographer;
@@ -68,52 +70,102 @@ public class PipSurfaceTransactionHelper {
* Operates the crop (and position) on a given transaction and leash
* @return same {@link PipSurfaceTransactionHelper} instance for method chaining
*/
- public PipSurfaceTransactionHelper crop(SurfaceControl.Transaction tx, SurfaceControl leash,
- Rect destinationBounds) {
+ public PipSurfaceTransactionHelper cropAndPosition(@NonNull SurfaceControl.Transaction tx,
+ @NonNull SurfaceControl leash, @NonNull Rect destinationBounds) {
tx.setWindowCrop(leash, destinationBounds.width(), destinationBounds.height())
.setPosition(leash, destinationBounds.left, destinationBounds.top);
return this;
}
/**
+ * Operates {@link SurfaceControl.Transaction#setCrop} on a given transaction and leash.
+ *
+ * @param tx the transaction to apply
+ * @param leash the leash to crop
+ * @param relativeDestinationBounds the bounds to crop, which is relative to the leash
+ * coordinate
+ * @return same {@link PipSurfaceTransactionHelper} instance for method chaining
+ */
+ public PipSurfaceTransactionHelper crop(@NonNull SurfaceControl.Transaction tx,
+ @NonNull SurfaceControl leash, @NonNull Rect relativeDestinationBounds) {
+ tx.setCrop(leash, relativeDestinationBounds);
+ return this;
+ }
+
+ /**
* Operates the scale (setMatrix) on a given transaction and leash
* @return same {@link PipSurfaceTransactionHelper} instance for method chaining
*/
public PipSurfaceTransactionHelper scale(SurfaceControl.Transaction tx, SurfaceControl leash,
Rect sourceBounds, Rect destinationBounds) {
mTmpDestinationRectF.set(destinationBounds);
- return scale(tx, leash, sourceBounds, mTmpDestinationRectF, 0 /* degrees */);
+ return scale(tx, leash, sourceBounds, mTmpDestinationRectF, 0 /* degrees */,
+ true /* shouldOffset */);
}
/**
* Operates the scale (setMatrix) on a given transaction and leash
* @return same {@link PipSurfaceTransactionHelper} instance for method chaining
*/
- public PipSurfaceTransactionHelper scale(SurfaceControl.Transaction tx, SurfaceControl leash,
- Rect sourceBounds, RectF destinationBounds) {
- return scale(tx, leash, sourceBounds, destinationBounds, 0 /* degrees */);
+ public PipSurfaceTransactionHelper scale(@NonNull SurfaceControl.Transaction tx,
+ @NonNull SurfaceControl leash, @NonNull Rect sourceBounds,
+ @NonNull RectF destinationBounds) {
+ return scale(tx, leash, sourceBounds, destinationBounds, 0 /* degrees */,
+ true /* shouldOffset */);
}
/**
- * Operates the scale (setMatrix) on a given transaction and leash
+ * Operates the scale (setMatrix) on a given transaction and leash.
+ *
+ * @param shouldOffset {@code true} to offset the leash to (0, 0)
* @return same {@link PipSurfaceTransactionHelper} instance for method chaining
*/
- public PipSurfaceTransactionHelper scale(SurfaceControl.Transaction tx, SurfaceControl leash,
- Rect sourceBounds, Rect destinationBounds, float degrees) {
+ public PipSurfaceTransactionHelper scale(@NonNull SurfaceControl.Transaction tx,
+ @NonNull SurfaceControl leash, @NonNull Rect sourceBounds,
+ @NonNull Rect destinationBounds, boolean shouldOffset) {
+ mTmpDestinationRectF.set(destinationBounds);
+ return scale(tx, leash, sourceBounds, mTmpDestinationRectF, 0 /* degrees */, shouldOffset);
+ }
+
+ /**
+ * Operates the scale (setMatrix) on a given transaction and leash.
+ *
+ * @return same {@link PipSurfaceTransactionHelper} instance for method chaining
+ */
+ public PipSurfaceTransactionHelper scale(@NonNull SurfaceControl.Transaction tx,
+ @NonNull SurfaceControl leash, @NonNull Rect sourceBounds,
+ @NonNull Rect destinationBounds, float degrees) {
+ return scale(tx, leash, sourceBounds, destinationBounds, degrees, true /* shouldOffset */);
+ }
+
+ /**
+ * Operates the scale (setMatrix) on a given transaction and leash.
+ *
+ * @param shouldOffset {@code true} to offset the leash to (0, 0)
+ * @return same {@link PipSurfaceTransactionHelper} instance for method chaining
+ */
+ public PipSurfaceTransactionHelper scale(@NonNull SurfaceControl.Transaction tx,
+ @NonNull SurfaceControl leash, @NonNull Rect sourceBounds,
+ @NonNull Rect destinationBounds, float degrees, boolean shouldOffset) {
mTmpDestinationRectF.set(destinationBounds);
- return scale(tx, leash, sourceBounds, mTmpDestinationRectF, degrees);
+ return scale(tx, leash, sourceBounds, mTmpDestinationRectF, degrees, shouldOffset);
}
/**
* Operates the scale (setMatrix) on a given transaction and leash, along with a rotation.
+ *
+ * @param shouldOffset {@code true} to offset the leash to (0, 0)
* @return same {@link PipSurfaceTransactionHelper} instance for method chaining
*/
- public PipSurfaceTransactionHelper scale(SurfaceControl.Transaction tx, SurfaceControl leash,
- Rect sourceBounds, RectF destinationBounds, float degrees) {
+ public PipSurfaceTransactionHelper scale(@NonNull SurfaceControl.Transaction tx,
+ @NonNull SurfaceControl leash, @NonNull Rect sourceBounds,
+ @NonNull RectF destinationBounds, float degrees, boolean shouldOffset) {
mTmpSourceRectF.set(sourceBounds);
// We want the matrix to position the surface relative to the screen coordinates so offset
- // the source to 0,0
- mTmpSourceRectF.offsetTo(0, 0);
+ // the source to (0, 0) if {@code shouldOffset} is true.
+ if (shouldOffset) {
+ mTmpSourceRectF.offsetTo(0, 0);
+ }
mTmpDestinationRectF.set(destinationBounds);
mTmpTransform.setRectToRect(mTmpSourceRectF, mTmpDestinationRectF, Matrix.ScaleToFit.FILL);
mTmpTransform.postRotate(degrees,
@@ -123,17 +175,19 @@ public class PipSurfaceTransactionHelper {
}
/**
- * Operates the scale (setMatrix) on a given transaction and leash
+ * Operates the scale (setMatrix) on a given transaction and leash.
+ *
+ * @param leashOffset the offset of the leash bounds relative to the screen coordinate
* @return same {@link PipSurfaceTransactionHelper} instance for method chaining
*/
- public PipSurfaceTransactionHelper scaleAndCrop(SurfaceControl.Transaction tx,
- SurfaceControl leash, Rect sourceRectHint,
- Rect sourceBounds, Rect destinationBounds, Rect insets,
- boolean isInPipDirection, float fraction) {
+ public PipSurfaceTransactionHelper scaleAndCrop(@NonNull SurfaceControl.Transaction tx,
+ @NonNull SurfaceControl leash, @NonNull Rect sourceRectHint, @NonNull Rect sourceBounds,
+ @NonNull Rect destinationBounds, @NonNull Rect insets, boolean isInPipDirection,
+ float fraction, @NonNull Point leashOffset) {
mTmpDestinationRect.set(sourceBounds);
// Similar to {@link #scale}, we want to position the surface relative to the screen
- // coordinates so offset the bounds to 0,0
- mTmpDestinationRect.offsetTo(0, 0);
+ // coordinates so offset the bounds relative to the leash.
+ mTmpDestinationRect.offset(-leashOffset.x, -leashOffset.y);
mTmpDestinationRect.inset(insets);
// Scale to the bounds no smaller than the destination and offset such that the top/left
// of the scaled inset source rect aligns with the top/left of the destination bounds
@@ -152,13 +206,13 @@ public class PipSurfaceTransactionHelper {
scale = Math.max((float) destinationBounds.width() / sourceBounds.width(),
(float) destinationBounds.height() / sourceBounds.height());
}
- float left = destinationBounds.left - insets.left * scale;
- float top = destinationBounds.top - insets.top * scale;
+ float left = destinationBounds.left - mTmpDestinationRect.left * scale;
+ float top = destinationBounds.top - mTmpDestinationRect.top * scale;
if (scale == 1) {
// Work around the 1 pixel off error by rounding the position down at very beginning.
// We noticed such error from flicker tests, not visually.
- left = sourceBounds.left;
- top = sourceBounds.top;
+ left = leashOffset.x;
+ top = leashOffset.y;
}
mTmpTransform.setScale(scale, scale);
tx.setMatrix(leash, mTmpTransform, mTmpFloat9)
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
index b4e03299f14c..c4e63dfdade9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
@@ -960,7 +960,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
final SurfaceControl.Transaction boundsChangeTx =
mSurfaceControlTransactionFactory.getTransaction();
mSurfaceTransactionHelper
- .crop(boundsChangeTx, mLeash, destinationBounds)
+ .cropAndPosition(boundsChangeTx, mLeash, destinationBounds)
.round(boundsChangeTx, mLeash, true /* applyCornerRadius */);
mPipTransitionState.setTransitionState(PipTransitionState.ENTRY_SCHEDULED);
@@ -988,7 +988,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction();
mSurfaceTransactionHelper
.resetScale(tx, mLeash, destinationBounds)
- .crop(tx, mLeash, destinationBounds)
+ .cropAndPosition(tx, mLeash, destinationBounds)
.round(tx, mLeash, isInPip());
// The animation is finished in the Launcher and here we directly apply the final touch.
applyEnterPipSyncTransaction(destinationBounds, () -> {
@@ -1525,7 +1525,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
mPipBoundsState.setBounds(toBounds);
final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction();
mSurfaceTransactionHelper
- .crop(tx, mLeash, toBounds)
+ .cropAndPosition(tx, mLeash, toBounds)
.round(tx, mLeash, mPipTransitionState.isInPip());
if (shouldSyncPipTransactionWithMenu()) {
mPipMenuController.resizePipMenu(mLeash, tx, toBounds);
@@ -1628,7 +1628,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
Rect destinationBounds) {
final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction();
mSurfaceTransactionHelper
- .crop(tx, mLeash, destinationBounds)
+ .cropAndPosition(tx, mLeash, destinationBounds)
.resetScale(tx, mLeash, destinationBounds)
.round(tx, mLeash, mPipTransitionState.isInPip());
return tx;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
index 6da39951efbe..9aa5066fc76c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
@@ -551,7 +551,7 @@ public class PipTransition extends PipTransitionController {
}
// Reset the scale with bounds change synchronously.
if (hasValidLeash) {
- mSurfaceTransactionHelper.crop(tx, leash, destinationBounds)
+ mSurfaceTransactionHelper.cropAndPosition(tx, leash, destinationBounds)
.resetScale(tx, leash, destinationBounds)
.round(tx, leash, true /* applyCornerRadius */);
final Rect appBounds = mPipOrganizer.mAppBounds;
@@ -588,7 +588,8 @@ public class PipTransition extends PipTransitionController {
ProtoLog.w(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
"%s: Destination bounds were changed during animation", TAG);
rotateBounds(finishBounds, displayBounds, mEndFixedRotation, displayRotation);
- mSurfaceTransactionHelper.crop(mFinishTransaction, leash, finishBounds);
+ mSurfaceTransactionHelper.cropAndPosition(mFinishTransaction, leash,
+ finishBounds);
}
}
mFinishTransaction = null;
@@ -1068,6 +1069,11 @@ public class PipTransition extends PipTransitionController {
mPipBoundsState.mayUseCachedLauncherShelfHeight();
final Rect destinationBounds = mPipBoundsAlgorithm.getEntryDestinationBounds();
final Rect currentBounds = pipChange.getStartAbsBounds();
+ // The app bounds should offset relative to the task leash to make the center calculation
+ // correctly.
+ final Rect relativeAppBounds = new Rect(taskInfo.topActivityMainWindowFrame != null
+ ? taskInfo.topActivityMainWindowFrame : currentBounds);
+ relativeAppBounds.offset(-currentBounds.left, -currentBounds.top);
int rotationDelta = deltaRotation(startRotation, endRotation);
Rect sourceHintRect = mPipOrganizer.takeSwipeSourceRectHint();
@@ -1089,6 +1095,8 @@ public class PipTransition extends PipTransitionController {
if (taskInfo.pictureInPictureParams != null
&& taskInfo.pictureInPictureParams.isAutoEnterEnabled()
&& mPipTransitionState.getInSwipePipToHomeTransition()) {
+ // TODO(b/356277166): add support to swipe PIP to home with
+ // non-match parent activity.
handleSwipePipToHomeTransition(startTransaction, finishTransaction, leash,
sourceHintRect, destinationBounds, taskInfo);
return;
@@ -1119,8 +1127,8 @@ public class PipTransition extends PipTransitionController {
// TODO(b/272819817): cleanup the null-check and extra logging.
final boolean hasTopActivityInfo = taskInfo.topActivityInfo != null;
if (hasTopActivityInfo && mFixedRotationState != FIXED_ROTATION_TRANSITION) {
- animator.setAppIconContentOverlay(
- mContext, currentBounds, destinationBounds, taskInfo.topActivityInfo,
+ animator.setAppIconContentOverlay(mContext, relativeAppBounds,
+ destinationBounds, taskInfo.topActivityInfo,
mPipBoundsState.getLauncherState().getAppIconSizePx());
} else {
ProtoLog.w(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
@@ -1149,14 +1157,14 @@ public class PipTransition extends PipTransitionController {
animationDuration = 0;
}
mSurfaceTransactionHelper
- .crop(finishTransaction, leash, destinationBounds)
+ .cropAndPosition(finishTransaction, leash, destinationBounds)
.round(finishTransaction, leash, true /* applyCornerRadius */);
// Always reset to bounds animation type afterwards.
setEnterAnimationType(ANIM_TYPE_BOUNDS);
} else {
throw new RuntimeException("Unrecognized animation type: " + enterAnimationType);
}
- mPipOrganizer.setContentOverlay(animator.getContentOverlayLeash(), currentBounds);
+ mPipOrganizer.setContentOverlay(animator.getContentOverlayLeash(), relativeAppBounds);
animator.setTransitionDirection(TRANSITION_DIRECTION_TO_PIP)
.setPipAnimationCallback(mPipAnimationCallback)
.setDuration(animationDuration);
@@ -1340,11 +1348,11 @@ public class PipTransition extends PipTransitionController {
"%s: Update pip for unhandled transition, change=%s, destBounds=%s, isInPip=%b",
TAG, pipChange, destBounds, isInPip);
mSurfaceTransactionHelper
- .crop(startTransaction, leash, destBounds)
+ .cropAndPosition(startTransaction, leash, destBounds)
.round(startTransaction, leash, isInPip)
.shadow(startTransaction, leash, isInPip);
mSurfaceTransactionHelper
- .crop(finishTransaction, leash, destBounds)
+ .cropAndPosition(finishTransaction, leash, destBounds)
.round(finishTransaction, leash, isInPip)
.shadow(finishTransaction, leash, isInPip);
// Make sure the PiP keeps invisible if it was faded out. If it needs to fade in, that will
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTransition.java
index 7a0e6694cb51..affa6aaa5d07 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTransition.java
@@ -338,7 +338,7 @@ public class TvPipTransition extends PipTransitionController {
final Rect pipBounds = mPipBoundsState.getBounds();
mSurfaceTransactionHelper
.resetScale(startTransaction, pipLeash, pipBounds)
- .crop(startTransaction, pipLeash, pipBounds)
+ .cropAndPosition(startTransaction, pipLeash, pipBounds)
.shadow(startTransaction, pipLeash, false);
final SurfaceControl.Transaction transaction = mTransactionFactory.getTransaction();
@@ -420,7 +420,7 @@ public class TvPipTransition extends PipTransitionController {
mSurfaceTransactionHelper
.resetScale(finishTransaction, leash, pipBounds)
- .crop(finishTransaction, leash, pipBounds)
+ .cropAndPosition(finishTransaction, leash, pipBounds)
.shadow(finishTransaction, leash, false);
final Rect currentBounds = pipChange.getStartAbsBounds();
@@ -443,7 +443,7 @@ public class TvPipTransition extends PipTransitionController {
SurfaceControl.Transaction tx = mTransactionFactory.getTransaction();
mSurfaceTransactionHelper
.resetScale(tx, leash, pipBounds)
- .crop(tx, leash, pipBounds)
+ .cropAndPosition(tx, leash, pipBounds)
.shadow(tx, leash, false);
mShellTaskOrganizer.applyTransaction(resizePipWct);
tx.apply();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipResizeAnimator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipResizeAnimator.java
index d565776c9917..012dabbbb9f8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipResizeAnimator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipResizeAnimator.java
@@ -17,6 +17,7 @@
package com.android.wm.shell.pip2.animation;
import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
import android.animation.RectEvaluator;
import android.animation.ValueAnimator;
import android.content.Context;
@@ -27,13 +28,13 @@ import android.view.SurfaceControl;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.wm.shell.pip2.PipSurfaceTransactionHelper;
/**
* Animator that handles any resize related animation for PIP.
*/
-public class PipResizeAnimator extends ValueAnimator
- implements ValueAnimator.AnimatorUpdateListener, Animator.AnimatorListener{
+public class PipResizeAnimator extends ValueAnimator {
@NonNull
private final Context mContext;
@NonNull
@@ -61,9 +62,47 @@ public class PipResizeAnimator extends ValueAnimator
private final Rect mAnimatedRect = new Rect();
private final float mDelta;
- private final PipSurfaceTransactionHelper.SurfaceControlTransactionFactory
+ private PipSurfaceTransactionHelper.SurfaceControlTransactionFactory
mSurfaceControlTransactionFactory;
+ private final Animator.AnimatorListener mAnimatorListener = new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ super.onAnimationStart(animation);
+ if (mAnimationStartCallback != null) {
+ mAnimationStartCallback.run();
+ }
+ if (mStartTx != null) {
+ setBoundsAndRotation(mStartTx, mLeash, mBaseBounds, mStartBounds, mDelta);
+ mStartTx.apply();
+ }
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ super.onAnimationEnd(animation);
+ if (mFinishTx != null) {
+ setBoundsAndRotation(mFinishTx, mLeash, mBaseBounds, mEndBounds, 0f);
+ }
+ if (mAnimationEndCallback != null) {
+ mAnimationEndCallback.run();
+ }
+ }
+ };
+
+ private final ValueAnimator.AnimatorUpdateListener mAnimatorUpdateListener =
+ new AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(@NonNull ValueAnimator animation) {
+ final SurfaceControl.Transaction tx =
+ mSurfaceControlTransactionFactory.getTransaction();
+ final float fraction = getAnimatedFraction();
+ final float degrees = (1.0f - fraction) * mDelta;
+ setBoundsAndRotation(tx, mLeash, mBaseBounds, mAnimatedRect, degrees);
+ tx.apply();
+ }
+ };
+
public PipResizeAnimator(@NonNull Context context,
@NonNull SurfaceControl leash,
@Nullable SurfaceControl.Transaction startTransaction,
@@ -89,8 +128,8 @@ public class PipResizeAnimator extends ValueAnimator
mRectEvaluator = new RectEvaluator(mAnimatedRect);
setObjectValues(startBounds, endBounds);
- addListener(this);
- addUpdateListener(this);
+ addListener(mAnimatorListener);
+ addUpdateListener(mAnimatorUpdateListener);
setEvaluator(mRectEvaluator);
setDuration(duration);
}
@@ -103,26 +142,6 @@ public class PipResizeAnimator extends ValueAnimator
mAnimationEndCallback = runnable;
}
- @Override
- public void onAnimationStart(@NonNull Animator animation) {
- if (mAnimationStartCallback != null) {
- mAnimationStartCallback.run();
- }
- if (mStartTx != null) {
- setBoundsAndRotation(mStartTx, mLeash, mBaseBounds, mStartBounds, mDelta);
- mStartTx.apply();
- }
- }
-
- @Override
- public void onAnimationUpdate(@NonNull ValueAnimator animation) {
- final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction();
- final float fraction = getAnimatedFraction();
- final float degrees = (1.0f - fraction) * mDelta;
- setBoundsAndRotation(tx, mLeash, mBaseBounds, mAnimatedRect, degrees);
- tx.apply();
- }
-
/**
* Set a proper transform matrix for a leash to move it to given bounds with a certain rotation.
*
@@ -130,7 +149,7 @@ public class PipResizeAnimator extends ValueAnimator
* @param targetBounds bounds to which we are scaling the leash.
* @param degrees degrees of rotation - counter-clockwise is positive by convention.
*/
- public static void setBoundsAndRotation(SurfaceControl.Transaction tx, SurfaceControl leash,
+ private static void setBoundsAndRotation(SurfaceControl.Transaction tx, SurfaceControl leash,
Rect baseBounds, Rect targetBounds, float degrees) {
Matrix transformTensor = new Matrix();
final float[] mMatrixTmp = new float[9];
@@ -144,19 +163,9 @@ public class PipResizeAnimator extends ValueAnimator
tx.setMatrix(leash, transformTensor, mMatrixTmp);
}
- @Override
- public void onAnimationEnd(@NonNull Animator animation) {
- if (mFinishTx != null) {
- setBoundsAndRotation(mFinishTx, mLeash, mBaseBounds, mEndBounds, 0f);
- }
- if (mAnimationEndCallback != null) {
- mAnimationEndCallback.run();
- }
+ @VisibleForTesting
+ void setSurfaceControlTransactionFactory(@NonNull
+ PipSurfaceTransactionHelper.SurfaceControlTransactionFactory factory) {
+ mSurfaceControlTransactionFactory = factory;
}
-
- @Override
- public void onAnimationCancel(@NonNull Animator animation) {}
-
- @Override
- public void onAnimationRepeat(@NonNull Animator animation) {}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/IRecentTasksListener.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/IRecentTasksListener.aidl
index 245829ecafb3..371bdd5c6469 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/IRecentTasksListener.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/IRecentTasksListener.aidl
@@ -45,4 +45,7 @@ oneway interface IRecentTasksListener {
/** A task has moved to front. */
oneway void onTaskMovedToFront(in RunningTaskInfo taskInfo);
+
+ /** A task info has changed. */
+ oneway void onTaskInfoChanged(in RunningTaskInfo taskInfo);
} \ No newline at end of file
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 6086801491e2..faa20159f64a 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
@@ -47,7 +47,6 @@ import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import com.android.internal.protolog.ProtoLog;
-import com.android.window.flags.Flags;
import com.android.wm.shell.common.ExternalInterfaceBinder;
import com.android.wm.shell.common.RemoteCallable;
import com.android.wm.shell.common.ShellExecutor;
@@ -289,6 +288,11 @@ public class RecentTasksController implements TaskStackListenerCallback,
}
@Override
+ public void onTaskChangedThroughTransition(@NonNull ActivityManager.RunningTaskInfo taskInfo) {
+ notifyTaskInfoChanged(taskInfo);
+ }
+
+ @Override
public void onTaskMovedToFrontThroughTransition(
ActivityManager.RunningTaskInfo runningTaskInfo) {
notifyTaskMovedToFront(runningTaskInfo);
@@ -355,6 +359,19 @@ public class RecentTasksController implements TaskStackListenerCallback,
}
}
+ private void notifyTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) {
+ if (mListener == null
+ || !DesktopModeFlags.ENABLE_TASK_STACK_OBSERVER_IN_SHELL.isTrue()
+ || taskInfo.realActivity == null) {
+ return;
+ }
+ try {
+ mListener.onTaskInfoChanged(taskInfo);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed call onTaskInfoChanged", e);
+ }
+ }
+
private void notifyTaskMovedToFront(ActivityManager.RunningTaskInfo taskInfo) {
if (mListener == null
|| !DesktopModeFlags.ENABLE_TASK_STACK_OBSERVER_IN_SHELL.isTrue()
@@ -426,7 +443,7 @@ public class RecentTasksController implements TaskStackListenerCallback,
// If task has their app bounds set to null which happens after reboot, set the
// app bounds to persisted lastFullscreenBounds. Also set the position in parent
// to the top left of the bounds.
- if (Flags.enableDesktopWindowingPersistence()
+ if (DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_PERSISTENCE.isTrue()
&& taskInfo.configuration.windowConfiguration.getAppBounds() == null) {
taskInfo.configuration.windowConfiguration.setAppBounds(
taskInfo.lastNonFullscreenBounds);
@@ -636,6 +653,11 @@ public class RecentTasksController implements TaskStackListenerCallback,
public void onTaskMovedToFront(ActivityManager.RunningTaskInfo taskInfo) {
mListener.call(l -> l.onTaskMovedToFront(taskInfo));
}
+
+ @Override
+ public void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) {
+ mListener.call(l -> l.onTaskInfoChanged(taskInfo));
+ }
};
public IRecentTasksImpl(RecentTasksController controller) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/TaskStackTransitionObserver.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/TaskStackTransitionObserver.kt
index 1af99f974a28..d28a462546f9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/TaskStackTransitionObserver.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/TaskStackTransitionObserver.kt
@@ -20,8 +20,9 @@ import android.app.ActivityManager.RunningTaskInfo
import android.os.IBinder
import android.util.ArrayMap
import android.view.SurfaceControl
-import android.window.TransitionInfo
+import android.view.WindowManager.TRANSIT_CHANGE
import android.window.DesktopModeFlags
+import android.window.TransitionInfo
import com.android.wm.shell.shared.TransitionUtil
import com.android.wm.shell.sysui.ShellInit
import com.android.wm.shell.transition.Transitions
@@ -69,8 +70,10 @@ class TaskStackTransitionObserver(
// Find the first task that is opening, this should be the one at the front after
// the transition
if (TransitionUtil.isOpeningType(change.mode)) {
- notifyTaskStackTransitionObserverListeners(taskInfo)
+ notifyOnTaskMovedToFront(taskInfo)
break
+ } else if (change.mode == TRANSIT_CHANGE) {
+ notifyOnTaskChanged(taskInfo)
}
}
}
@@ -95,15 +98,23 @@ class TaskStackTransitionObserver(
taskStackTransitionObserverListeners.remove(taskStackTransitionObserverListener)
}
- private fun notifyTaskStackTransitionObserverListeners(taskInfo: RunningTaskInfo) {
+ private fun notifyOnTaskMovedToFront(taskInfo: RunningTaskInfo) {
taskStackTransitionObserverListeners.forEach { (listener, executor) ->
executor.execute { listener.onTaskMovedToFrontThroughTransition(taskInfo) }
}
}
+ private fun notifyOnTaskChanged(taskInfo: RunningTaskInfo) {
+ taskStackTransitionObserverListeners.forEach { (listener, executor) ->
+ executor.execute { listener.onTaskChangedThroughTransition(taskInfo) }
+ }
+ }
+
/** Listener to use to get updates regarding task stack from this observer */
interface TaskStackTransitionObserverListener {
/** Called when a task is moved to front. */
fun onTaskMovedToFrontThroughTransition(taskInfo: RunningTaskInfo) {}
+ /** Called when a task info has changed. */
+ fun onTaskChangedThroughTransition(taskInfo: RunningTaskInfo) {}
}
}
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 3946b6173b0b..c9546731a193 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
@@ -180,12 +180,13 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL
// all other cases, it is expected that the transition handler positions and crops the task
// in order to allow the handler time to animate before the task before the final
// position and crop are set.
- final boolean shouldSetTaskPositionAndCrop = mTaskDragResizer.isResizingOrAnimating();
+ final boolean shouldSetTaskVisibilityPositionAndCrop =
+ mTaskDragResizer.isResizingOrAnimating();
// Use |applyStartTransactionOnDraw| so that the transaction (that applies task crop) is
// synced with the buffer transaction (that draws the View). Both will be shown on screen
// at the same, whereas applying them independently causes flickering. See b/270202228.
relayout(taskInfo, t, t, true /* applyStartTransactionOnDraw */,
- shouldSetTaskPositionAndCrop, hasGlobalFocus);
+ shouldSetTaskVisibilityPositionAndCrop, hasGlobalFocus);
}
@VisibleForTesting
@@ -193,7 +194,7 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL
RelayoutParams relayoutParams,
ActivityManager.RunningTaskInfo taskInfo,
boolean applyStartTransactionOnDraw,
- boolean setTaskCropAndPosition,
+ boolean shouldSetTaskVisibilityPositionAndCrop,
boolean isStatusBarVisible,
boolean isKeyguardVisibleAndOccluded,
InsetsState displayInsetsState,
@@ -206,7 +207,7 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL
? R.dimen.freeform_decor_shadow_focused_thickness
: R.dimen.freeform_decor_shadow_unfocused_thickness;
relayoutParams.mApplyStartTransactionOnDraw = applyStartTransactionOnDraw;
- relayoutParams.mSetTaskPositionAndCrop = setTaskCropAndPosition;
+ relayoutParams.mSetTaskVisibilityPositionAndCrop = shouldSetTaskVisibilityPositionAndCrop;
relayoutParams.mIsCaptionVisible = taskInfo.isFreeform()
|| (isStatusBarVisible && !isKeyguardVisibleAndOccluded);
@@ -234,7 +235,7 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL
@SuppressLint("MissingPermission")
void relayout(RunningTaskInfo taskInfo,
SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT,
- boolean applyStartTransactionOnDraw, boolean setTaskCropAndPosition,
+ boolean applyStartTransactionOnDraw, boolean shouldSetTaskVisibilityPositionAndCrop,
boolean hasGlobalFocus) {
final boolean isFreeform =
taskInfo.getWindowingMode() == WindowConfiguration.WINDOWING_MODE_FREEFORM;
@@ -246,7 +247,8 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL
final WindowContainerTransaction wct = new WindowContainerTransaction();
updateRelayoutParams(mRelayoutParams, taskInfo, applyStartTransactionOnDraw,
- setTaskCropAndPosition, mIsStatusBarVisible, mIsKeyguardVisibleAndOccluded,
+ shouldSetTaskVisibilityPositionAndCrop, mIsStatusBarVisible,
+ mIsKeyguardVisibleAndOccluded,
mDisplayController.getInsetsState(taskInfo.displayId), hasGlobalFocus);
relayout(mRelayoutParams, startT, finishT, wct, oldRootView, mResult);
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 a3324cc6f286..29b8ddd03970 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
@@ -130,7 +130,6 @@ 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.additionalviewcontainer.AdditionalSystemViewContainer;
import com.android.wm.shell.windowdecor.extension.InsetsStateKt;
import com.android.wm.shell.windowdecor.extension.TaskInfoKt;
import com.android.wm.shell.windowdecor.viewholder.AppHeaderViewHolder;
@@ -181,7 +180,6 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
private boolean mTransitionDragActive;
private SparseArray<EventReceiver> mEventReceiversByDisplay = new SparseArray<>();
- private DesktopStatusBarInputLayerSupplier mStatusBarInputLayerSupplier;
private final ExclusionRegionListener mExclusionRegionListener =
new ExclusionRegionListenerImpl();
@@ -420,11 +418,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
return Unit.INSTANCE;
});
}
- if (Flags.enableHandleInputFix()) {
- mStatusBarInputLayerSupplier =
- new DesktopStatusBarInputLayerSupplier(mContext, mMainHandler);
- mFocusTransitionObserver.setLocalFocusTransitionListener(this, mMainExecutor);
- }
+ mFocusTransitionObserver.setLocalFocusTransitionListener(this, mMainExecutor);
}
@Override
@@ -480,7 +474,6 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
removeTaskFromEventReceiver(oldTaskInfo.displayId);
incrementEventReceiverTasks(taskInfo.displayId);
}
- decoration.setStatusBarInputLayer(getStatusBarInputLayer(taskInfo));
decoration.relayout(taskInfo, decoration.mHasGlobalFocus);
mActivityOrientationChangeHandler.ifPresent(handler ->
handler.handleActivityOrientationChange(oldTaskInfo, taskInfo));
@@ -519,7 +512,6 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
if (decoration == null) {
createWindowDecoration(taskInfo, taskSurface, startT, finishT);
} else {
- decoration.setStatusBarInputLayer(getStatusBarInputLayer(taskInfo));
decoration.relayout(taskInfo, startT, finishT, false /* applyStartTransactionOnDraw */,
false /* shouldSetTaskPositionAndCrop */,
mFocusTransitionObserver.hasGlobalFocus(taskInfo));
@@ -673,7 +665,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
decoration.closeHandleMenu();
// When the app enters split-select, the handle will no longer be visible, meaning
// we shouldn't receive input for it any longer.
- decoration.detachStatusBarInputLayer();
+ decoration.disposeStatusBarInputLayer();
mDesktopTasksController.requestSplit(decoration.mTaskInfo, false /* leftOrTop */);
}
@@ -1314,8 +1306,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
// should not be receiving any input.
if (resultType == TO_SPLIT_LEFT_INDICATOR
|| resultType == TO_SPLIT_RIGHT_INDICATOR) {
- relevantDecor.detachStatusBarInputLayer();
- // We should also detach the other split task's input layer if
+ relevantDecor.disposeStatusBarInputLayer();
+ // We should also dispose the other split task's input layer if
// applicable.
final int splitPosition = mSplitScreenController
.getSplitPosition(relevantDecor.mTaskInfo.taskId);
@@ -1328,7 +1320,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
mSplitScreenController.getTaskInfo(oppositePosition);
if (oppositeTaskInfo != null) {
mWindowDecorByTaskId.get(oppositeTaskInfo.taskId)
- .detachStatusBarInputLayer();
+ .disposeStatusBarInputLayer();
}
}
}
@@ -1578,7 +1570,6 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
touchEventListener, touchEventListener, touchEventListener, touchEventListener);
windowDecoration.setExclusionRegionListener(mExclusionRegionListener);
windowDecoration.setDragPositioningCallback(taskPositioner);
- windowDecoration.setStatusBarInputLayer(getStatusBarInputLayer(taskInfo));
windowDecoration.relayout(taskInfo, startT, finishT,
false /* applyStartTransactionOnDraw */, false /* shouldSetTaskPositionAndCrop */,
mFocusTransitionObserver.hasGlobalFocus(taskInfo));
@@ -1587,18 +1578,6 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
}
}
- /** Decide which cached status bar input layer should be used for a decoration. */
- private AdditionalSystemViewContainer getStatusBarInputLayer(
- RunningTaskInfo taskInfo
- ) {
- if (mStatusBarInputLayerSupplier == null) return null;
- return mStatusBarInputLayerSupplier.getStatusBarInputLayer(
- taskInfo,
- mSplitScreenController.getSplitPosition(taskInfo.taskId),
- mSplitScreenController.isLeftRightSplit()
- );
- }
-
private RunningTaskInfo getOtherSplitTask(int taskId) {
@SplitPosition int remainingTaskPosition = mSplitScreenController
.getSplitPosition(taskId) == SPLIT_POSITION_BOTTOM_OR_RIGHT
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 9f37358b6044..c88ac3d19635 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
@@ -106,7 +106,6 @@ import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
import com.android.wm.shell.shared.desktopmode.DesktopModeTransitionSource;
import com.android.wm.shell.shared.desktopmode.ManageWindowsViewContainer;
import com.android.wm.shell.splitscreen.SplitScreenController;
-import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalSystemViewContainer;
import com.android.wm.shell.windowdecor.extension.TaskInfoKt;
import com.android.wm.shell.windowdecor.viewholder.AppHandleViewHolder;
import com.android.wm.shell.windowdecor.viewholder.AppHeaderViewHolder;
@@ -206,7 +205,6 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
private final MultiInstanceHelper mMultiInstanceHelper;
private final WindowDecorCaptionHandleRepository mWindowDecorCaptionHandleRepository;
private final DesktopRepository mDesktopRepository;
- private AdditionalSystemViewContainer mStatusBarInputLayer;
DesktopModeWindowDecoration(
Context context,
@@ -392,18 +390,25 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
@Override
void relayout(ActivityManager.RunningTaskInfo taskInfo, boolean hasGlobalFocus) {
final SurfaceControl.Transaction t = mSurfaceControlTransactionSupplier.get();
- // The crop and position of the task should only be set when a task is fluid resizing. In
- // all other cases, it is expected that the transition handler positions and crops the task
- // in order to allow the handler time to animate before the task before the final
- // position and crop are set.
- final boolean shouldSetTaskPositionAndCrop = !DesktopModeStatus.isVeiledResizeEnabled()
- && mTaskDragResizer.isResizingOrAnimating();
+ // The visibility, crop and position of the task should only be set when a task is
+ // fluid resizing. In all other cases, it is expected that the transition handler sets
+ // those task properties to allow the handler time to animate with full control of the task
+ // leash. In general, allowing the window decoration to set any of these is likely to cause
+ // incorrect frames and flickering because relayouts from TaskListener#onTaskInfoChanged
+ // aren't synchronized with shell transition callbacks, so if they come too early it
+ // might show/hide or crop the task at a bad time.
+ // Fluid resizing is exempt from this because it intentionally doesn't use shell
+ // transitions to resize the task, so onTaskInfoChanged relayouts is the only way to make
+ // sure the crop is set correctly.
+ final boolean shouldSetTaskVisibilityPositionAndCrop =
+ !DesktopModeStatus.isVeiledResizeEnabled()
+ && mTaskDragResizer.isResizingOrAnimating();
// For headers only (i.e. in freeform): use |applyStartTransactionOnDraw| so that the
// transaction (that applies task crop) is synced with the buffer transaction (that draws
// the View). Both will be shown on screen at the same, whereas applying them independently
// causes flickering. See b/270202228.
final boolean applyTransactionOnDraw = taskInfo.isFreeform();
- relayout(taskInfo, t, t, applyTransactionOnDraw, shouldSetTaskPositionAndCrop,
+ relayout(taskInfo, t, t, applyTransactionOnDraw, shouldSetTaskVisibilityPositionAndCrop,
hasGlobalFocus);
if (!applyTransactionOnDraw) {
t.apply();
@@ -430,19 +435,19 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
void relayout(ActivityManager.RunningTaskInfo taskInfo,
SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT,
- boolean applyStartTransactionOnDraw, boolean shouldSetTaskPositionAndCrop,
+ boolean applyStartTransactionOnDraw, boolean shouldSetTaskVisibilityPositionAndCrop,
boolean hasGlobalFocus) {
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,
- shouldSetTaskPositionAndCrop, hasGlobalFocus);
+ shouldSetTaskVisibilityPositionAndCrop, hasGlobalFocus);
} 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,
- shouldSetTaskPositionAndCrop, hasGlobalFocus);
+ shouldSetTaskVisibilityPositionAndCrop, hasGlobalFocus);
}
Trace.endSection();
}
@@ -450,12 +455,12 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
/** Run the whole relayout phase immediately without delay. */
private void relayoutInSync(ActivityManager.RunningTaskInfo taskInfo,
SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT,
- boolean applyStartTransactionOnDraw, boolean shouldSetTaskPositionAndCrop,
+ boolean applyStartTransactionOnDraw, boolean shouldSetTaskVisibilityPositionAndCrop,
boolean hasGlobalFocus) {
// Clear the current ViewHost runnable as we will update the ViewHost here
clearCurrentViewHostRunnable();
updateRelayoutParamsAndSurfaces(taskInfo, startT, finishT, applyStartTransactionOnDraw,
- shouldSetTaskPositionAndCrop, hasGlobalFocus);
+ shouldSetTaskVisibilityPositionAndCrop, hasGlobalFocus);
if (mResult.mRootView != null) {
updateViewHost(mRelayoutParams, startT, mResult);
}
@@ -477,7 +482,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
*/
private void relayoutWithDelayedViewHost(ActivityManager.RunningTaskInfo taskInfo,
SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT,
- boolean applyStartTransactionOnDraw, boolean shouldSetTaskPositionAndCrop,
+ boolean applyStartTransactionOnDraw, boolean shouldSetTaskVisibilityPositionAndCrop,
boolean hasGlobalFocus) {
if (applyStartTransactionOnDraw) {
throw new IllegalArgumentException(
@@ -486,7 +491,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
// Clear the current ViewHost runnable as we will update the ViewHost here
clearCurrentViewHostRunnable();
updateRelayoutParamsAndSurfaces(taskInfo, startT, finishT,
- false /* applyStartTransactionOnDraw */, shouldSetTaskPositionAndCrop,
+ false /* applyStartTransactionOnDraw */, shouldSetTaskVisibilityPositionAndCrop,
hasGlobalFocus);
if (mResult.mRootView == null) {
// This means something blocks the window decor from showing, e.g. the task is hidden.
@@ -501,7 +506,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
@SuppressLint("MissingPermission")
private void updateRelayoutParamsAndSurfaces(ActivityManager.RunningTaskInfo taskInfo,
SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT,
- boolean applyStartTransactionOnDraw, boolean shouldSetTaskPositionAndCrop,
+ boolean applyStartTransactionOnDraw, boolean shouldSetTaskVisibilityPositionAndCrop,
boolean hasGlobalFocus) {
Trace.beginSection("DesktopModeWindowDecoration#updateRelayoutParamsAndSurfaces");
@@ -526,9 +531,9 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
final boolean inFullImmersive = mDesktopRepository
.isTaskInFullImmersiveState(taskInfo.taskId);
updateRelayoutParams(mRelayoutParams, mContext, taskInfo, applyStartTransactionOnDraw,
- shouldSetTaskPositionAndCrop, mIsStatusBarVisible, mIsKeyguardVisibleAndOccluded,
- inFullImmersive, mDisplayController.getInsetsState(taskInfo.displayId),
- hasGlobalFocus);
+ shouldSetTaskVisibilityPositionAndCrop, mIsStatusBarVisible,
+ mIsKeyguardVisibleAndOccluded, inFullImmersive,
+ mDisplayController.getInsetsState(taskInfo.displayId), hasGlobalFocus);
final WindowDecorLinearLayout oldRootView = mResult.mRootView;
final SurfaceControl oldDecorationSurface = mDecorationContainerSurface;
@@ -550,13 +555,13 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
notifyNoCaptionHandle();
}
mExclusionRegionListener.onExclusionRegionDismissed(mTaskInfo.taskId);
- detachStatusBarInputLayer();
+ disposeStatusBarInputLayer();
Trace.endSection(); // DesktopModeWindowDecoration#updateRelayoutParamsAndSurfaces
return;
}
if (oldRootView != mResult.mRootView) {
- detachStatusBarInputLayer();
+ disposeStatusBarInputLayer();
mWindowDecorViewHolder = createViewHolder();
}
Trace.beginSection("DesktopModeWindowDecoration#relayout-binding");
@@ -574,9 +579,6 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
mTaskInfo, position, mResult.mCaptionWidth, mResult.mCaptionHeight,
isCaptionVisible()
));
- if (mStatusBarInputLayer != null) {
- asAppHandle(mWindowDecorViewHolder).bindStatusBarInputLayer(mStatusBarInputLayer);
- }
} else {
mWindowDecorViewHolder.bindData(new AppHeaderViewHolder.HeaderData(
mTaskInfo,
@@ -790,15 +792,15 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
}
/**
- * Detach the status bar input layer from this decoration. Intended to be
+ * Dispose of the view used to forward inputs in status bar region. Intended to be
* used any time handle is no longer visible.
*/
- void detachStatusBarInputLayer() {
+ void disposeStatusBarInputLayer() {
if (!isAppHandle(mWindowDecorViewHolder)
|| !Flags.enableHandleInputFix()) {
return;
}
- asAppHandle(mWindowDecorViewHolder).detachStatusBarInputLayer();
+ asAppHandle(mWindowDecorViewHolder).disposeStatusBarInputLayer();
}
private WindowDecorationViewHolder createViewHolder() {
@@ -857,7 +859,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
Context context,
ActivityManager.RunningTaskInfo taskInfo,
boolean applyStartTransactionOnDraw,
- boolean shouldSetTaskPositionAndCrop,
+ boolean shouldSetTaskVisibilityPositionAndCrop,
boolean isStatusBarVisible,
boolean isKeyguardVisibleAndOccluded,
boolean inFullImmersiveMode,
@@ -953,7 +955,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
: R.dimen.freeform_decor_shadow_unfocused_thickness;
}
relayoutParams.mApplyStartTransactionOnDraw = applyStartTransactionOnDraw;
- relayoutParams.mSetTaskPositionAndCrop = shouldSetTaskPositionAndCrop;
+ relayoutParams.mSetTaskVisibilityPositionAndCrop = shouldSetTaskVisibilityPositionAndCrop;
// The configuration used to layout the window decoration. A copy is made instead of using
// the original reference so that the configuration isn't mutated on config changes and
@@ -1094,13 +1096,13 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
if (mAppIconBitmap != null && mAppName != null) {
return;
}
- final ComponentName baseActivity = mTaskInfo.baseActivity;
- if (baseActivity == null) {
- Slog.e(TAG, "Base activity component not found in task");
+ if (mTaskInfo.baseIntent == null) {
+ Slog.e(TAG, "Base intent not found in task");
return;
}
final PackageManager pm = mUserContext.getPackageManager();
- final ActivityInfo activityInfo = pm.getActivityInfo(baseActivity, 0 /* flags */);
+ 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,
@@ -1638,7 +1640,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
closeManageWindowsMenu();
mExclusionRegionListener.onExclusionRegionDismissed(mTaskInfo.taskId);
disposeResizeVeil();
- detachStatusBarInputLayer();
+ disposeStatusBarInputLayer();
clearCurrentViewHostRunnable();
if (canEnterDesktopMode(mContext) && Flags.enableDesktopWindowingAppHandleEducation()) {
notifyNoCaptionHandle();
@@ -1755,16 +1757,6 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
+ "}";
}
- /**
- * Set the view container to be used to forward input through status bar. Null in cases
- * where input forwarding isn't needed.
- */
- public void setStatusBarInputLayer(
- @Nullable AdditionalSystemViewContainer additionalSystemViewContainer
- ) {
- mStatusBarInputLayer = additionalSystemViewContainer;
- }
-
static class Factory {
DesktopModeWindowDecoration create(
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopStatusBarInputLayerSupplier.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopStatusBarInputLayerSupplier.kt
deleted file mode 100644
index 9c5215d1249c..000000000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopStatusBarInputLayerSupplier.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.wm.shell.windowdecor
-
-import android.app.ActivityManager.RunningTaskInfo
-import android.app.WindowConfiguration
-import android.content.Context
-import android.graphics.PixelFormat
-import android.os.Handler
-import android.view.Gravity
-import android.view.View
-import android.view.WindowManager
-import com.android.wm.shell.shared.annotations.ShellMainThread
-import com.android.wm.shell.shared.split.SplitScreenConstants
-import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalSystemViewContainer
-
-/**
- * Supplier for [AdditionalSystemViewContainer] objects to be used for forwarding input
- * events through status bar to an app handle. Currently supports two simultaneous input layers.
- *
- * The supplier will pick one of two input layer view containers to use: one for tasks in
- * fullscreen or top/left split stage, and one for tasks in right split stage.
- */
-class DesktopStatusBarInputLayerSupplier(
- private val context: Context,
- @ShellMainThread handler: Handler
-) {
- private val inputLayers: MutableList<AdditionalSystemViewContainer> = mutableListOf()
-
- init {
- // Post this as creation of the input layer views is a relatively expensive operation.
- handler.post {
- repeat(TOTAL_INPUT_LAYERS) {
- inputLayers.add(createInputLayer())
- }
- }
- }
-
- private fun createInputLayer(): AdditionalSystemViewContainer {
- val lp = WindowManager.LayoutParams(
- WindowManager.LayoutParams.TYPE_STATUS_BAR_ADDITIONAL,
- WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
- PixelFormat.TRANSPARENT
- )
- lp.title = "Desktop status bar input layer"
- lp.gravity = Gravity.LEFT or Gravity.TOP
- lp.setTrustedOverlay()
-
- // Make this window a spy window to enable it to pilfer pointers from the system-wide
- // gesture listener that receives events before window. This is to prevent notification
- // shade gesture when we swipe down to enter desktop.
- lp.inputFeatures = WindowManager.LayoutParams.INPUT_FEATURE_SPY
- lp.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
- val view = View(context)
- view.visibility = View.GONE
- return AdditionalSystemViewContainer(
- WindowManagerWrapper(
- context.getSystemService<WindowManager>(WindowManager::class.java)
- ),
- view,
- lp
- )
- }
-
- /**
- * Decide which cached status bar input layer should be used for a decoration, if any.
- *
- * [splitPosition] and [isLeftRightSplit] are used to determine which input layer we use.
- * The first one is reserved for fullscreen tasks or tasks in top/left split,
- * while the second one is exclusively used for tasks in right split stage. Note we care about
- * left-right vs top-bottom split as the bottom stage should not use an input layer.
- */
- fun getStatusBarInputLayer(
- taskInfo: RunningTaskInfo,
- @SplitScreenConstants.SplitPosition splitPosition: Int,
- isLeftRightSplit: Boolean
- ): AdditionalSystemViewContainer? {
- if (!taskInfo.isVisibleRequested) return null
- // Fullscreen and top/left split tasks will use the first input layer.
- if (taskInfo.windowingMode == WindowConfiguration.WINDOWING_MODE_FULLSCREEN
- || splitPosition == SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT
- ) {
- return inputLayers[LEFT_TOP_INPUT_LAYER]
- }
- // Right split tasks will use the second one.
- if (isLeftRightSplit && splitPosition == SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT
- ) {
- return inputLayers[RIGHT_SPLIT_INPUT_LAYER]
- }
- // Which leaves bottom split and freeform tasks, which do not need an input layer
- // as the status bar is not blocking them.
- return null
- }
-
- companion object {
- private const val TOTAL_INPUT_LAYERS = 2
- // Input layer index for fullscreen tasks and tasks in top-left split
- private const val LEFT_TOP_INPUT_LAYER = 0
- // Input layer index for tasks in right split stage. Does not include bottom split as that
- // stage is not blocked by status bar.
- private const val RIGHT_SPLIT_INPUT_LAYER = 1
- }
-}
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 f97dfb89bc0d..b016c755e323 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
@@ -249,7 +249,9 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
if (!mTaskInfo.isVisible) {
releaseViews(wct);
- finishT.hide(mTaskSurface);
+ if (params.mSetTaskVisibilityPositionAndCrop) {
+ finishT.hide(mTaskSurface);
+ }
return;
}
@@ -422,7 +424,7 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
private void updateTaskSurface(RelayoutParams params, SurfaceControl.Transaction startT,
SurfaceControl.Transaction finishT, RelayoutResult<T> outResult) {
- if (params.mSetTaskPositionAndCrop) {
+ if (params.mSetTaskVisibilityPositionAndCrop) {
final Point taskPosition = mTaskInfo.positionInParent;
startT.setWindowCrop(mTaskSurface, outResult.mWidth, outResult.mHeight);
finishT.setWindowCrop(mTaskSurface, outResult.mWidth, outResult.mHeight)
@@ -437,9 +439,13 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
shadowRadius =
loadDimension(mDecorWindowContext.getResources(), params.mShadowRadiusId);
}
- startT.setShadowRadius(mTaskSurface, shadowRadius).show(mTaskSurface);
+ startT.setShadowRadius(mTaskSurface, shadowRadius);
finishT.setShadowRadius(mTaskSurface, shadowRadius);
+ if (params.mSetTaskVisibilityPositionAndCrop) {
+ startT.show(mTaskSurface);
+ }
+
if (mTaskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM) {
if (!DesktopModeStatus.isVeiledResizeEnabled()) {
// When fluid resize is enabled, add a background to freeform tasks
@@ -758,7 +764,7 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
Configuration mWindowDecorConfig;
boolean mApplyStartTransactionOnDraw;
- boolean mSetTaskPositionAndCrop;
+ boolean mSetTaskVisibilityPositionAndCrop;
boolean mHasGlobalFocus;
void reset() {
@@ -777,7 +783,7 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
mIsCaptionVisible = false;
mApplyStartTransactionOnDraw = false;
- mSetTaskPositionAndCrop = false;
+ mSetTaskVisibilityPositionAndCrop = false;
mWindowDecorConfig = null;
mHasGlobalFocus = false;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/additionalviewcontainer/AdditionalSystemViewContainer.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/additionalviewcontainer/AdditionalSystemViewContainer.kt
index 1451f363ec73..8b6aaaf619e0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/additionalviewcontainer/AdditionalSystemViewContainer.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/additionalviewcontainer/AdditionalSystemViewContainer.kt
@@ -23,8 +23,8 @@ import android.view.Gravity
import android.view.LayoutInflater
import android.view.SurfaceControl
import android.view.View
+import android.view.WindowInsets
import android.view.WindowManager
-import android.view.WindowManager.LayoutParams
import com.android.wm.shell.windowdecor.WindowManagerWrapper
/**
@@ -33,11 +33,27 @@ import com.android.wm.shell.windowdecor.WindowManagerWrapper
*/
class AdditionalSystemViewContainer(
private val windowManagerWrapper: WindowManagerWrapper,
- override val view: View,
- val lp: LayoutParams
+ taskId: Int,
+ x: Int,
+ y: Int,
+ width: Int,
+ height: Int,
+ flags: Int,
+ @WindowInsets.Type.InsetsType forciblyShownTypes: Int = 0,
+ override val view: View
) : AdditionalViewContainer() {
+ val lp: WindowManager.LayoutParams = WindowManager.LayoutParams(
+ width, height, x, y,
+ WindowManager.LayoutParams.TYPE_STATUS_BAR_ADDITIONAL,
+ flags,
+ PixelFormat.TRANSPARENT
+ ).apply {
+ title = "Additional view container of Task=$taskId"
+ gravity = Gravity.LEFT or Gravity.TOP
+ setTrustedOverlay()
+ this.forciblyShownTypes = forciblyShownTypes
+ }
- /** Provide a layout id of a view to inflate for this view container. */
constructor(
context: Context,
windowManagerWrapper: WindowManagerWrapper,
@@ -50,30 +66,15 @@ class AdditionalSystemViewContainer(
@LayoutRes layoutId: Int
) : this(
windowManagerWrapper = windowManagerWrapper,
- view = LayoutInflater.from(context).inflate(layoutId, null /* parent */),
- lp = createLayoutParams(x, y, width, height, flags, taskId)
+ taskId = taskId,
+ x = x,
+ y = y,
+ width = width,
+ height = height,
+ flags = flags,
+ view = LayoutInflater.from(context).inflate(layoutId, null /* parent */)
)
- /** Provide a view directly for this view container */
- constructor(
- windowManagerWrapper: WindowManagerWrapper,
- taskId: Int,
- x: Int,
- y: Int,
- width: Int,
- height: Int,
- flags: Int,
- view: View,
- forciblyShownTypes: Int = 0
- ) : this(
- windowManagerWrapper = windowManagerWrapper,
- view = view,
- lp = createLayoutParams(x, y, width, height, flags, taskId).apply {
- this.forciblyShownTypes = forciblyShownTypes
- }
- )
-
- /** Do not supply a view at all, instead creating the view container with a basic view. */
constructor(
context: Context,
windowManagerWrapper: WindowManagerWrapper,
@@ -85,7 +86,12 @@ class AdditionalSystemViewContainer(
flags: Int
) : this(
windowManagerWrapper = windowManagerWrapper,
- lp = createLayoutParams(x, y, width, height, flags, taskId),
+ taskId = taskId,
+ x = x,
+ y = y,
+ width = width,
+ height = height,
+ flags = flags,
view = View(context)
)
@@ -98,7 +104,7 @@ class AdditionalSystemViewContainer(
}
override fun setPosition(t: SurfaceControl.Transaction, x: Float, y: Float) {
- lp.apply {
+ val lp = (view.layoutParams as WindowManager.LayoutParams).apply {
this.x = x.toInt()
this.y = y.toInt()
}
@@ -118,29 +124,13 @@ class AdditionalSystemViewContainer(
): AdditionalSystemViewContainer =
AdditionalSystemViewContainer(
windowManagerWrapper = windowManagerWrapper,
- view = view,
- lp = createLayoutParams(x, y, width, height, flags, taskId)
+ taskId = taskId,
+ x = x,
+ y = y,
+ width = width,
+ height = height,
+ flags = flags,
+ view = view
)
}
- companion object {
- fun createLayoutParams(
- x: Int,
- y: Int,
- width: Int,
- height: Int,
- flags: Int,
- taskId: Int
- ): LayoutParams {
- return LayoutParams(
- width, height, x, y,
- LayoutParams.TYPE_STATUS_BAR_ADDITIONAL,
- flags,
- PixelFormat.TRANSPARENT
- ).apply {
- title = "Additional view container of Task=$taskId"
- gravity = Gravity.LEFT or Gravity.TOP
- setTrustedOverlay()
- }
- }
- }
}
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 ff418c6daa02..e43c3a613157 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
@@ -21,10 +21,13 @@ import android.app.ActivityManager.RunningTaskInfo
import android.content.Context
import android.graphics.Rect
import android.util.SparseArray
+import android.window.DisplayAreaInfo
+import android.window.WindowContainerTransaction
import androidx.core.util.valueIterator
import com.android.internal.annotations.VisibleForTesting
import com.android.wm.shell.RootTaskDisplayAreaOrganizer
import com.android.wm.shell.ShellTaskOrganizer
+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.DesktopRepository
@@ -45,10 +48,16 @@ class DesktopTilingDecorViewModel(
private val toggleResizeDesktopTaskTransitionHandler: ToggleResizeDesktopTaskTransitionHandler,
private val returnToDragStartAnimator: ReturnToDragStartAnimator,
private val taskRepository: DesktopRepository,
-) {
+) : DisplayChangeController.OnDisplayChangingListener {
@VisibleForTesting
var tilingTransitionHandlerByDisplayId = SparseArray<DesktopTilingWindowDecoration>()
+ init {
+ // TODO(b/374309287): Move this interface implementation to
+ // [DesktopModeWindowDecorViewModel] when the migration is done.
+ displayController.addDisplayChangingController(this)
+ }
+
fun snapToHalfScreen(
taskInfo: ActivityManager.RunningTaskInfo,
desktopModeWindowDecoration: DesktopModeWindowDecoration,
@@ -102,7 +111,20 @@ class DesktopTilingDecorViewModel(
fun onUserChange() {
for (tilingHandler in tilingTransitionHandlerByDisplayId.valueIterator()) {
- tilingHandler.onUserChange()
+ tilingHandler.resetTilingSession()
}
}
+
+ override fun onDisplayChange(
+ displayId: Int,
+ fromRotation: Int,
+ toRotation: Int,
+ newDisplayAreaInfo: DisplayAreaInfo?,
+ t: WindowContainerTransaction?,
+ ) {
+ // Exit if the rotation hasn't changed or is changed by 180 degrees. [fromRotation] and
+ // [toRotation] can be one of the [@Surface.Rotation] values.
+ if ((fromRotation % 2 == toRotation % 2)) return
+ tilingTransitionHandlerByDisplayId.get(displayId)?.resetTilingSession()
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDividerWindowManager.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDividerWindowManager.kt
index 9bf1304f2b39..209eb5e501b2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDividerWindowManager.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDividerWindowManager.kt
@@ -23,6 +23,7 @@ import android.graphics.Rect
import android.graphics.Region
import android.os.Binder
import android.view.LayoutInflater
+import android.view.RoundedCorner
import android.view.SurfaceControl
import android.view.SurfaceControlViewHost
import android.view.View
@@ -53,12 +54,14 @@ class DesktopTilingDividerWindowManager(
private val transitionHandler: DesktopTilingWindowDecoration,
private val transactionSupplier: Supplier<SurfaceControl.Transaction>,
private var dividerBounds: Rect,
+ private val displayContext: Context,
) : WindowlessWindowManager(config, leash, null), DividerMoveCallback, View.OnLayoutChangeListener {
private lateinit var viewHost: SurfaceControlViewHost
private var tilingDividerView: TilingDividerView? = null
private var dividerShown = false
private var handleRegionWidth: Int = -1
private var setTouchRegion = true
+ private val maxRoundedCornerRadius = getMaxRoundedCornerRadius()
/**
* Gets bounds of divider window with screen based coordinate on the param Rect.
@@ -93,7 +96,11 @@ class DesktopTilingDividerWindowManager(
getDividerBounds(tmpDividerBounds)
dividerView.setup(this, tmpDividerBounds)
t.setRelativeLayer(leash, relativeLeash, 1)
- .setPosition(leash, dividerBounds.left.toFloat(), dividerBounds.top.toFloat())
+ .setPosition(
+ leash,
+ dividerBounds.left.toFloat() - maxRoundedCornerRadius,
+ dividerBounds.top.toFloat(),
+ )
.show(leash)
syncQueue.runInSync { transaction ->
transaction.merge(t)
@@ -144,7 +151,7 @@ class DesktopTilingDividerWindowManager(
*/
override fun onDividerMove(pos: Int): Boolean {
val t = transactionSupplier.get()
- t.setPosition(leash, pos.toFloat(), dividerBounds.top.toFloat())
+ t.setPosition(leash, pos.toFloat() - maxRoundedCornerRadius, dividerBounds.top.toFloat())
val dividerWidth = dividerBounds.width()
dividerBounds.set(pos, dividerBounds.top, pos + dividerWidth, dividerBounds.bottom)
return transitionHandler.onDividerHandleMoved(dividerBounds, t)
@@ -157,7 +164,7 @@ class DesktopTilingDividerWindowManager(
override fun onDividerMovedEnd(pos: Int) {
setSlippery(true)
val t = transactionSupplier.get()
- t.setPosition(leash, pos.toFloat(), dividerBounds.top.toFloat())
+ t.setPosition(leash, pos.toFloat() - maxRoundedCornerRadius, dividerBounds.top.toFloat())
val dividerWidth = dividerBounds.width()
dividerBounds.set(pos, dividerBounds.top, pos + dividerWidth, dividerBounds.bottom)
transitionHandler.onDividerHandleDragEnd(dividerBounds, t)
@@ -166,7 +173,7 @@ class DesktopTilingDividerWindowManager(
private fun getWindowManagerParams(): WindowManager.LayoutParams {
val lp =
WindowManager.LayoutParams(
- dividerBounds.width(),
+ dividerBounds.width() + 2 * maxRoundedCornerRadius,
dividerBounds.height(),
TYPE_DOCK_DIVIDER,
FLAG_NOT_FOCUSABLE or
@@ -225,4 +232,15 @@ class DesktopTilingDividerWindowManager(
}
viewHost.relayout(lp)
}
+
+ private fun getMaxRoundedCornerRadius(): Int {
+ val display = displayContext.display
+ return listOf(
+ RoundedCorner.POSITION_TOP_LEFT,
+ RoundedCorner.POSITION_TOP_RIGHT,
+ RoundedCorner.POSITION_BOTTOM_RIGHT,
+ RoundedCorner.POSITION_BOTTOM_LEFT,
+ )
+ .maxOf { position -> display.getRoundedCorner(position)?.getRadius() ?: 0 }
+ }
}
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 a7087aec3bb5..c46767c3a51d 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
@@ -195,6 +195,7 @@ class DesktopTilingWindowDecoration(
val builder = SurfaceControl.Builder()
rootTdaOrganizer.attachToDisplayArea(displayId, builder)
val leash = builder.setName(TILING_DIVIDER_TAG).setContainerLayer().build()
+ val displayContext = displayController.getDisplayContext(displayId) ?: return null
val tilingManager =
displayLayout?.let {
dividerBounds = inflateDividerBounds(it)
@@ -207,6 +208,7 @@ class DesktopTilingWindowDecoration(
this,
transactionSupplier,
dividerBounds,
+ displayContext,
)
}
// a leash to present the divider on top of, without re-parenting.
@@ -483,7 +485,7 @@ class DesktopTilingWindowDecoration(
}
}
- fun onUserChange() {
+ fun resetTilingSession() {
if (leftTaskResizingHelper != null) {
removeTask(leftTaskResizingHelper, taskVanished = false, shouldDelayUpdate = true)
leftTaskResizingHelper = null
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/TilingDividerView.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/TilingDividerView.kt
index f8113c219bd1..89229051941c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/TilingDividerView.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/TilingDividerView.kt
@@ -118,7 +118,7 @@ class TilingDividerView : FrameLayout, View.OnTouchListener, DragDetector.Motion
val dividerSize = resources.getDimensionPixelSize(R.dimen.split_divider_bar_width)
val backgroundLeft = (width - dividerSize) / 2
val backgroundTop = 0
- val backgroundRight = left + dividerSize
+ val backgroundRight = backgroundLeft + dividerSize
val backgroundBottom = height
backgroundRect.set(backgroundLeft, backgroundTop, backgroundRight, backgroundBottom)
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolder.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolder.kt
index c4e49466343e..b5700ffb046b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolder.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolder.kt
@@ -36,10 +36,13 @@ import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction
import android.widget.ImageButton
import androidx.core.view.ViewCompat
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat
+import com.android.internal.policy.SystemBarUtils
+import com.android.window.flags.Flags
import com.android.wm.shell.R
import com.android.wm.shell.shared.animation.Interpolators
import com.android.wm.shell.windowdecor.WindowManagerWrapper
import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalSystemViewContainer
+import com.android.wm.shell.windowdecor.viewholder.WindowDecorationViewHolder.Data
/**
* A desktop mode window decoration used when the window is in full "focus" (i.e. fullscreen/split).
@@ -66,12 +69,10 @@ internal class AppHandleViewHolder(
) : Data()
private lateinit var taskInfo: RunningTaskInfo
- private val position: Point = Point()
- private var width: Int = 0
- private var height: Int = 0
private val captionView: View = rootView.requireViewById(R.id.desktop_mode_caption)
private val captionHandle: ImageButton = rootView.requireViewById(R.id.caption_handle)
private val inputManager = context.getSystemService(InputManager::class.java)
+ private var statusBarInputLayerExists = false
// An invisible View that takes up the same coordinates as captionHandle but is layered
// above the status bar. The purpose of this View is to receive input intended for
@@ -111,54 +112,21 @@ internal class AppHandleViewHolder(
) {
captionHandle.imageTintList = ColorStateList.valueOf(getCaptionHandleBarColor(taskInfo))
this.taskInfo = taskInfo
- this.position.set(position)
- this.width = width
- this.height = height
- if (!isCaptionVisible && statusBarInputLayer != null) {
- detachStatusBarInputLayer()
+ // If handle is not in status bar region(i.e., bottom stage in vertical split),
+ // do not create an input layer
+ if (position.y >= SystemBarUtils.getStatusBarHeight(context)) return
+ if (!isCaptionVisible && statusBarInputLayerExists) {
+ disposeStatusBarInputLayer()
return
}
- }
-
- fun bindStatusBarInputLayer(
- statusBarLayer: AdditionalSystemViewContainer
- ) {
- // Input layer view modification takes a significant amount of time;
+ // Input layer view creation / modification takes a significant amount of time;
// post them so we don't hold up DesktopModeWindowDecoration#relayout.
- if (statusBarLayer == statusBarInputLayer) {
+ if (statusBarInputLayerExists) {
handler.post { updateStatusBarInputLayer(position) }
- return
- }
- // Remove the old input layer when changing to a new one.
- if (statusBarInputLayer != null) detachStatusBarInputLayer()
- if (statusBarLayer.view.visibility == View.GONE) {
- statusBarLayer.view.visibility = View.VISIBLE
- }
- statusBarInputLayer = statusBarLayer
- statusBarInputLayer?.let {
- inputLayer -> setupAppHandleA11y(inputLayer.view)
- }
- handler.post {
- val view = statusBarInputLayer?.view
- ?: error("Unable to find statusBarInputLayer View")
- // Caption handle is located within the status bar region, meaning the
- // DisplayPolicy will attempt to transfer this input to status bar if it's
- // a swipe down. Pilfer here to keep the gesture in handle alone.
- view.setOnTouchListener { v, event ->
- if (event.actionMasked == ACTION_DOWN) {
- inputManager.pilferPointers(v.viewRootImpl.inputToken)
- }
- captionHandle.dispatchTouchEvent(event)
- return@setOnTouchListener true
- }
- view.setOnHoverListener { _, event ->
- captionHandle.onHoverEvent(event)
- }
- val lp = statusBarInputLayer?.view?.layoutParams as WindowManager.LayoutParams
- lp.x = position.x
- lp.y = position.y
- lp.width = width
- lp.height = height
+ } else {
+ // Input layer is created on a delay; prevent multiple from being created.
+ statusBarInputLayerExists = true
+ handler.post { createStatusBarInputLayer(position, width, height) }
}
}
@@ -170,6 +138,40 @@ internal class AppHandleViewHolder(
animateCaptionHandleAlpha(startValue = 0f, endValue = 1f)
}
+ private fun createStatusBarInputLayer(handlePosition: Point,
+ handleWidth: Int,
+ handleHeight: Int) {
+ if (!Flags.enableHandleInputFix()) return
+ statusBarInputLayer = AdditionalSystemViewContainer(context, windowManagerWrapper,
+ taskInfo.taskId, handlePosition.x, handlePosition.y, handleWidth, handleHeight,
+ WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+ )
+ val view = statusBarInputLayer?.view ?: error("Unable to find statusBarInputLayer View")
+ val lp = statusBarInputLayer?.lp ?: error("Unable to find statusBarInputLayer " +
+ "LayoutParams")
+ lp.title = "Handle Input Layer of task " + taskInfo.taskId
+ lp.setTrustedOverlay()
+ // Make this window a spy window to enable it to pilfer pointers from the system-wide
+ // gesture listener that receives events before window. This is to prevent notification
+ // shade gesture when we swipe down to enter desktop.
+ lp.inputFeatures = WindowManager.LayoutParams.INPUT_FEATURE_SPY
+ view.setOnHoverListener { _, event ->
+ captionHandle.onHoverEvent(event)
+ }
+ // Caption handle is located within the status bar region, meaning the
+ // DisplayPolicy will attempt to transfer this input to status bar if it's
+ // a swipe down. Pilfer here to keep the gesture in handle alone.
+ view.setOnTouchListener { v, event ->
+ if (event.actionMasked == ACTION_DOWN) {
+ inputManager.pilferPointers(v.viewRootImpl.inputToken)
+ }
+ captionHandle.dispatchTouchEvent(event)
+ return@setOnTouchListener true
+ }
+ setupAppHandleA11y(view)
+ windowManagerWrapper.updateViewLayout(view, lp)
+ }
+
private fun setupAppHandleA11y(view: View) {
view.accessibilityDelegate = object : View.AccessibilityDelegate() {
override fun onInitializeAccessibilityNodeInfo(
@@ -222,12 +224,15 @@ internal class AppHandleViewHolder(
}
/**
- * Remove the input listeners from the input layer and remove it from this view holder.
+ * Remove the input layer from [WindowManager]. Should be used when caption handle
+ * is not visible.
*/
- fun detachStatusBarInputLayer() {
- statusBarInputLayer?.view?.setOnTouchListener(null)
- statusBarInputLayer?.view?.setOnHoverListener(null)
- statusBarInputLayer = null
+ fun disposeStatusBarInputLayer() {
+ statusBarInputLayerExists = false
+ handler.post {
+ statusBarInputLayer?.releaseView()
+ statusBarInputLayer = null
+ }
}
private fun getCaptionHandleBarColor(taskInfo: RunningTaskInfo): Int {
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 72d4dc6ffac9..13a8518ae8ed 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
@@ -700,7 +700,7 @@ public class BackAnimationControllerTest extends ShellTestCase {
eq(tInfo), eq(st), eq(ft), eq(callback));
mBackTransitionHandler.onAnimationFinished();
- final TransitionInfo.Change openToClose = createAppChange(openTaskId, TRANSIT_CLOSE,
+ final TransitionInfo.Change openToClose = createAppChangeFromChange(open, TRANSIT_CLOSE,
FLAG_BACK_GESTURE_ANIMATED);
tInfo2 = createTransitionInfo(TRANSIT_CLOSE_PREPARE_BACK_NAVIGATION, openToClose);
mBackTransitionHandler.mClosePrepareTransition = mock(IBinder.class);
@@ -830,6 +830,16 @@ public class BackAnimationControllerTest extends ShellTestCase {
return change;
}
+ private TransitionInfo.Change createAppChangeFromChange(
+ TransitionInfo.Change originalChange, @TransitionInfo.TransitionMode int mode,
+ @TransitionInfo.ChangeFlags int flags) {
+ final TransitionInfo.Change change = new TransitionInfo.Change(
+ originalChange.getTaskInfo().token, originalChange.getLeash());
+ change.setMode(mode);
+ change.setFlags(flags);
+ return change;
+ }
+
private static TransitionInfo createTransitionInfo(
@WindowManager.TransitionType int type, TransitionInfo.Change ... changes) {
final TransitionInfo info = new TransitionInfo(type, 0);
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 868883d12ac0..df061e368071 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
@@ -39,9 +39,12 @@ import androidx.test.filters.SmallTest
import com.android.internal.jank.Cuj.CUJ_DESKTOP_MODE_EXIT_MODE_ON_LAST_WINDOW_CLOSE
import com.android.internal.jank.InteractionJankMonitor
import com.android.window.flags.Flags
+import com.android.wm.shell.RootTaskDisplayAreaOrganizer
import com.android.wm.shell.ShellTestCase
import com.android.wm.shell.TestRunningTaskInfoBuilder
+import com.android.wm.shell.desktopmode.DesktopMixedTransitionHandler.PendingMixedTransition
import com.android.wm.shell.freeform.FreeformTaskTransitionHandler
+import com.android.wm.shell.sysui.ShellInit
import com.android.wm.shell.transition.Transitions
import com.google.common.truth.Truth.assertThat
import org.junit.Assert.assertFalse
@@ -51,7 +54,9 @@ import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.anyInt
import org.mockito.Mock
+import org.mockito.Mockito.times
import org.mockito.Mockito.verify
import org.mockito.kotlin.any
import org.mockito.kotlin.anyOrNull
@@ -80,6 +85,8 @@ class DesktopMixedTransitionHandlerTest : ShellTestCase() {
@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
private lateinit var mixedHandler: DesktopMixedTransitionHandler
@@ -94,7 +101,9 @@ class DesktopMixedTransitionHandlerTest : ShellTestCase() {
closeDesktopTaskTransitionHandler,
desktopImmersiveController,
interactionJankMonitor,
- mockHandler
+ mockHandler,
+ shellInit,
+ rootTaskDisplayAreaOrganizer,
)
}
@@ -238,8 +247,10 @@ class DesktopMixedTransitionHandlerTest : ShellTestCase() {
}
@Test
- @DisableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP)
- fun startLaunchTransition_immersiveMixDisabled_doesNotUseMixedHandler() {
+ @DisableFlags(
+ Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP,
+ Flags.FLAG_ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS)
+ fun startLaunchTransition_immersiveAndAppLaunchFlagsDisabled_doesNotUseMixedHandler() {
val wct = WindowContainerTransaction()
val task = createTask(WINDOWING_MODE_FREEFORM)
whenever(transitions.startTransition(eq(TRANSIT_OPEN), eq(wct), anyOrNull()))
@@ -274,6 +285,24 @@ class DesktopMixedTransitionHandlerTest : ShellTestCase() {
}
@Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS)
+ fun startLaunchTransition_desktopAppLaunchEnabled_usesMixedHandler() {
+ val wct = WindowContainerTransaction()
+ val task = createTask(WINDOWING_MODE_FREEFORM)
+ whenever(transitions.startTransition(eq(TRANSIT_OPEN), eq(wct), anyOrNull()))
+ .thenReturn(Binder())
+
+ mixedHandler.startLaunchTransition(
+ transitionType = TRANSIT_OPEN,
+ wct = wct,
+ taskId = task.taskId,
+ exitingImmersiveTask = null
+ )
+
+ verify(transitions).startTransition(TRANSIT_OPEN, wct, mixedHandler)
+ }
+
+ @Test
@EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP)
fun startAndAnimateLaunchTransition_withoutImmersiveChange_dispatchesAllChangesToLeftOver() {
val wct = WindowContainerTransaction()
@@ -355,6 +384,134 @@ class DesktopMixedTransitionHandlerTest : ShellTestCase() {
}
@Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS)
+ fun startAndAnimateLaunchTransition_noMinimizeChange_doesNotReparentMinimizeChange() {
+ val wct = WindowContainerTransaction()
+ val launchingTask = createTask(WINDOWING_MODE_FREEFORM)
+ val launchTaskChange = createChange(launchingTask)
+ val transition = Binder()
+ whenever(transitions.startTransition(eq(TRANSIT_OPEN), eq(wct), anyOrNull()))
+ .thenReturn(transition)
+
+ mixedHandler.startLaunchTransition(
+ transitionType = TRANSIT_OPEN,
+ wct = wct,
+ taskId = launchingTask.taskId,
+ minimizingTaskId = null,
+ )
+ mixedHandler.startAnimation(
+ transition,
+ createTransitionInfo(
+ TRANSIT_OPEN,
+ listOf(launchTaskChange)
+ ),
+ SurfaceControl.Transaction(),
+ SurfaceControl.Transaction(),
+ ) { }
+
+ verify(rootTaskDisplayAreaOrganizer, times(0))
+ .reparentToDisplayArea(anyInt(), any(), any())
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS)
+ fun startAndAnimateLaunchTransition_withMinimizeChange_reparentsMinimizeChange() {
+ val wct = WindowContainerTransaction()
+ val launchingTask = createTask(WINDOWING_MODE_FREEFORM)
+ val minimizingTask = createTask(WINDOWING_MODE_FREEFORM)
+ val launchTaskChange = createChange(launchingTask)
+ val minimizeChange = createChange(minimizingTask)
+ val transition = Binder()
+ whenever(transitions.startTransition(eq(TRANSIT_OPEN), eq(wct), anyOrNull()))
+ .thenReturn(transition)
+
+ mixedHandler.startLaunchTransition(
+ transitionType = TRANSIT_OPEN,
+ wct = wct,
+ taskId = launchingTask.taskId,
+ minimizingTaskId = minimizingTask.taskId,
+ )
+ mixedHandler.startAnimation(
+ transition,
+ createTransitionInfo(
+ TRANSIT_OPEN,
+ listOf(launchTaskChange, minimizeChange)
+ ),
+ SurfaceControl.Transaction(),
+ SurfaceControl.Transaction(),
+ ) { }
+
+ verify(rootTaskDisplayAreaOrganizer).reparentToDisplayArea(
+ anyInt(), eq(minimizeChange.leash), any())
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS)
+ fun addPendingAndAnimateLaunchTransition_noMinimizeChange_doesNotReparentMinimizeChange() {
+ val wct = WindowContainerTransaction()
+ val launchingTask = createTask(WINDOWING_MODE_FREEFORM)
+ val launchTaskChange = createChange(launchingTask)
+ val transition = Binder()
+ whenever(transitions.startTransition(eq(TRANSIT_OPEN), eq(wct), anyOrNull()))
+ .thenReturn(transition)
+
+ mixedHandler.addPendingMixedTransition(
+ PendingMixedTransition.Launch(
+ transition = transition,
+ launchingTask = launchingTask.taskId,
+ minimizingTask = null,
+ exitingImmersiveTask = null,
+ )
+ )
+ mixedHandler.startAnimation(
+ transition,
+ createTransitionInfo(
+ TRANSIT_OPEN,
+ listOf(launchTaskChange)
+ ),
+ SurfaceControl.Transaction(),
+ SurfaceControl.Transaction(),
+ ) { }
+
+ verify(rootTaskDisplayAreaOrganizer, times(0))
+ .reparentToDisplayArea(anyInt(), any(), any())
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS)
+ fun addPendingAndAnimateLaunchTransition_withMinimizeChange_reparentsMinimizeChange() {
+ val wct = WindowContainerTransaction()
+ val launchingTask = createTask(WINDOWING_MODE_FREEFORM)
+ val minimizingTask = createTask(WINDOWING_MODE_FREEFORM)
+ val launchTaskChange = createChange(launchingTask)
+ val minimizeChange = createChange(minimizingTask)
+ val transition = Binder()
+ whenever(transitions.startTransition(eq(TRANSIT_OPEN), eq(wct), anyOrNull()))
+ .thenReturn(transition)
+
+ mixedHandler.addPendingMixedTransition(
+ PendingMixedTransition.Launch(
+ transition = transition,
+ launchingTask = launchingTask.taskId,
+ minimizingTask = minimizingTask.taskId,
+ exitingImmersiveTask = null,
+ )
+ )
+ mixedHandler.startAnimation(
+ transition,
+ createTransitionInfo(
+ TRANSIT_OPEN,
+ listOf(launchTaskChange, minimizeChange)
+ ),
+ SurfaceControl.Transaction(),
+ SurfaceControl.Transaction(),
+ ) { }
+
+ verify(rootTaskDisplayAreaOrganizer).reparentToDisplayArea(
+ anyInt(), eq(minimizeChange.leash), any())
+ }
+
+ @Test
@EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP)
fun startAndAnimateLaunchTransition_removesPendingMixedTransition() {
val wct = WindowContainerTransaction()
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 fc89b0e127ae..b157d557c1d8 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
@@ -1413,7 +1413,8 @@ class DesktopTasksControllerTest : ShellTestCase() {
eq(TRANSIT_TO_FRONT),
any(),
eq(freeformTasks[0].taskId),
- anyOrNull()
+ anyOrNull(),
+ anyOrNull(),
)).thenReturn(Binder())
controller.moveTaskToFront(freeformTasks[0], remoteTransition = null)
@@ -1471,7 +1472,7 @@ class DesktopTasksControllerTest : ShellTestCase() {
val task = createTaskInfo(1001)
whenever(shellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(null)
whenever(desktopMixedTransitionHandler
- .startLaunchTransition(eq(TRANSIT_OPEN), any(), eq(task.taskId), anyOrNull()))
+ .startLaunchTransition(eq(TRANSIT_OPEN), any(), eq(task.taskId), anyOrNull(), anyOrNull()))
.thenReturn(Binder())
controller.moveTaskToFront(task.taskId, remoteTransition = null)
@@ -3692,7 +3693,8 @@ class DesktopTasksControllerTest : ShellTestCase() {
runOnTransitionStart = runOnStartTransit,
))
whenever(desktopMixedTransitionHandler
- .startLaunchTransition(any(), any(), anyInt(), anyOrNull())).thenReturn(transition)
+ .startLaunchTransition(any(), any(), anyInt(), anyOrNull(), anyOrNull()))
+ .thenReturn(transition)
controller.moveTaskToFront(task.taskId, remoteTransition = null)
@@ -3713,7 +3715,7 @@ class DesktopTasksControllerTest : ShellTestCase() {
runOnTransitionStart = runOnStartTransit,
))
whenever(desktopMixedTransitionHandler
- .startLaunchTransition(any(), any(), eq(task.taskId), anyOrNull()))
+ .startLaunchTransition(any(), any(), eq(task.taskId), anyOrNull(), anyOrNull()))
.thenReturn(transition)
controller.moveTaskToFront(task.taskId, remoteTransition = null)
@@ -4102,7 +4104,7 @@ class DesktopTasksControllerTest : ShellTestCase() {
val arg: ArgumentCaptor<WindowContainerTransaction> =
ArgumentCaptor.forClass(WindowContainerTransaction::class.java)
verify(desktopMixedTransitionHandler)
- .startLaunchTransition(eq(type), capture(arg), anyInt(), anyOrNull())
+ .startLaunchTransition(eq(type), capture(arg), anyInt(), anyOrNull(), anyOrNull())
return arg.value
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/ExitDesktopTaskTransitionHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/ExitDesktopTaskTransitionHandlerTest.java
index fefa933c5208..a82e5e8434b2 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/ExitDesktopTaskTransitionHandlerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/ExitDesktopTaskTransitionHandlerTest.java
@@ -19,8 +19,6 @@ package com.android.wm.shell.desktopmode;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-import static androidx.test.internal.runner.junit4.statement.UiThreadStatement.runOnUiThread;
-
import static com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_EXIT_DESKTOP_MODE_UNKNOWN;
import static org.junit.Assert.assertTrue;
@@ -28,6 +26,7 @@ import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
+import android.animation.AnimatorTestRule;
import android.annotation.NonNull;
import android.app.ActivityManager;
import android.app.WindowConfiguration;
@@ -36,6 +35,8 @@ import android.content.res.Resources;
import android.graphics.Point;
import android.os.Handler;
import android.os.IBinder;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
import android.util.DisplayMetrics;
import android.view.SurfaceControl;
import android.view.WindowManager;
@@ -53,17 +54,23 @@ import com.android.wm.shell.shared.desktopmode.DesktopModeTransitionSource;
import com.android.wm.shell.transition.Transitions;
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 java.util.ArrayList;
import java.util.function.Supplier;
/** Tests of {@link com.android.wm.shell.desktopmode.ExitDesktopTaskTransitionHandler} */
@SmallTest
+@TestableLooper.RunWithLooper
+@RunWith(AndroidTestingRunner.class)
public class ExitDesktopTaskTransitionHandlerTest extends ShellTestCase {
+ @Rule
+ public final AnimatorTestRule mAnimatorTestRule = new AnimatorTestRule(this);
+
@Mock
private Transitions mTransitions;
@Mock
@@ -105,7 +112,7 @@ public class ExitDesktopTaskTransitionHandlerTest extends ShellTestCase {
}
@Test
- public void testTransitExitDesktopModeAnimation() throws Throwable {
+ public void testTransitExitDesktopModeAnimation() {
final int transitionType = TRANSIT_EXIT_DESKTOP_MODE_UNKNOWN;
final int taskId = 1;
WindowContainerTransaction wct = new WindowContainerTransaction();
@@ -118,21 +125,16 @@ public class ExitDesktopTaskTransitionHandlerTest extends ShellTestCase {
TransitionInfo.Change change =
createChange(WindowManager.TRANSIT_CHANGE, taskId, WINDOWING_MODE_FULLSCREEN);
TransitionInfo info = createTransitionInfo(TRANSIT_EXIT_DESKTOP_MODE_UNKNOWN, change);
- ArrayList<Exception> exceptions = new ArrayList<>();
- runOnUiThread(() -> {
- try {
- assertTrue(mExitDesktopTaskTransitionHandler
- .startAnimation(mToken, info,
- new SurfaceControl.Transaction(),
- new SurfaceControl.Transaction(),
- mTransitionFinishCallback));
- } catch (Exception e) {
- exceptions.add(e);
- }
- });
- if (!exceptions.isEmpty()) {
- throw exceptions.get(0);
- }
+
+ final boolean animated = mExitDesktopTaskTransitionHandler
+ .startAnimation(mToken, info,
+ new SurfaceControl.Transaction(),
+ new SurfaceControl.Transaction(),
+ mTransitionFinishCallback);
+ mAnimatorTestRule.advanceTimeBy(
+ ExitDesktopTaskTransitionHandler.FULLSCREEN_ANIMATION_DURATION);
+
+ assertTrue(animated);
}
private TransitionInfo.Change createChange(@WindowManager.TransitionType int type, int taskId,
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 54198876955a..7dbadc9d9083 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
@@ -68,7 +68,6 @@ import org.mockito.kotlin.whenever
@SmallTest
@RunWith(AndroidTestingRunner::class)
@OptIn(ExperimentalCoroutinesApi::class)
-@Ignore("b/374328725")
class AppHandleEducationControllerTest : ShellTestCase() {
@JvmField
@Rule
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
index e74c804d4f40..bcb7461bfae7 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
@@ -280,7 +280,7 @@ public class PipTaskOrganizerTest extends ShellTestCase {
private void preparePipSurfaceTransactionHelper() {
doReturn(mMockPipSurfaceTransactionHelper).when(mMockPipSurfaceTransactionHelper)
- .crop(any(), any(), any());
+ .cropAndPosition(any(), any(), any());
doReturn(mMockPipSurfaceTransactionHelper).when(mMockPipSurfaceTransactionHelper)
.resetScale(any(), any(), any());
doReturn(mMockPipSurfaceTransactionHelper).when(mMockPipSurfaceTransactionHelper)
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipResizeAnimatorTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipResizeAnimatorTest.java
new file mode 100644
index 000000000000..0adb50b81896
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipResizeAnimatorTest.java
@@ -0,0 +1,276 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.pip2.animation;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.when;
+import static org.mockito.kotlin.MatchersKt.eq;
+import static org.junit.Assert.assertEquals;
+
+import android.content.Context;
+import android.graphics.Matrix;
+import android.graphics.Rect;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.view.SurfaceControl;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.wm.shell.pip2.PipSurfaceTransactionHelper;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Unit test against {@link PipResizeAnimator}.
+ */
+@SmallTest
+@TestableLooper.RunWithLooper
+@RunWith(AndroidTestingRunner.class)
+public class PipResizeAnimatorTest {
+
+ private static final float FLOAT_COMPARISON_DELTA = 0.001f;
+
+ @Mock private Context mMockContext;
+
+ @Mock private PipSurfaceTransactionHelper.SurfaceControlTransactionFactory mMockFactory;
+
+ @Mock private SurfaceControl.Transaction mMockTransaction;
+
+ @Mock private SurfaceControl.Transaction mMockStartTransaction;
+
+ @Mock private SurfaceControl.Transaction mMockFinishTransaction;
+
+ @Mock private Runnable mMockStartCallback;
+
+ @Mock private Runnable mMockEndCallback;
+
+ private PipResizeAnimator mPipResizeAnimator;
+ private Rect mBaseBounds;
+ private Rect mStartBounds;
+ private Rect mEndBounds;
+ private SurfaceControl mTestLeash;
+ private ArgumentCaptor<Matrix> mArgumentCaptor;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ when(mMockFactory.getTransaction()).thenReturn(mMockTransaction);
+ when(mMockTransaction.setMatrix(any(SurfaceControl.class), any(Matrix.class), any()))
+ .thenReturn(mMockTransaction);
+ when(mMockStartTransaction.setMatrix(any(SurfaceControl.class), any(Matrix.class), any()))
+ .thenReturn(mMockStartTransaction);
+ when(mMockFinishTransaction.setMatrix(any(SurfaceControl.class), any(Matrix.class), any()))
+ .thenReturn(mMockFinishTransaction);
+
+ mArgumentCaptor = ArgumentCaptor.forClass(Matrix.class);
+ mTestLeash = new SurfaceControl.Builder()
+ .setContainerLayer()
+ .setName("PipResizeAnimatorTest")
+ .setCallsite("PipResizeAnimatorTest")
+ .build();
+ }
+
+ @Test
+ public void setAnimationStartCallback_resize_callbackStartCallback() {
+ mBaseBounds = new Rect(100, 100, 500, 500);
+ mStartBounds = new Rect(200, 200, 1_000, 1_000);
+ mEndBounds = new Rect(mBaseBounds);
+ final int duration = 10;
+ final float delta = 0;
+ mPipResizeAnimator = new PipResizeAnimator(mMockContext, mTestLeash,
+ mMockStartTransaction, mMockFinishTransaction,
+ mBaseBounds, mStartBounds, mEndBounds,
+ duration, delta);
+
+ mPipResizeAnimator.setAnimationStartCallback(mMockStartCallback);
+ mPipResizeAnimator.setAnimationEndCallback(mMockEndCallback);
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+ mPipResizeAnimator.start();
+ mPipResizeAnimator.pause();
+ });
+
+ verify(mMockStartCallback).run();
+ verifyZeroInteractions(mMockEndCallback);
+ }
+
+ @Test
+ public void setAnimationEndCallback_resize_callbackStartAndEndCallback() {
+ mBaseBounds = new Rect(100, 100, 500, 500);
+ mStartBounds = new Rect(200, 200, 1_000, 1_000);
+ mEndBounds = new Rect(mBaseBounds);
+ final int duration = 10;
+ final float delta = 0;
+ mPipResizeAnimator = new PipResizeAnimator(mMockContext, mTestLeash,
+ mMockStartTransaction, mMockFinishTransaction,
+ mBaseBounds, mStartBounds, mEndBounds,
+ duration, delta);
+
+ mPipResizeAnimator.setAnimationStartCallback(mMockStartCallback);
+ mPipResizeAnimator.setAnimationEndCallback(mMockEndCallback);
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+ mPipResizeAnimator.start();
+ mPipResizeAnimator.end();
+ });
+
+ verify(mMockStartCallback).run();
+ verify(mMockEndCallback).run();
+ }
+
+ @Test
+ public void onAnimationEnd_resizeDown_sizeChanged() {
+ // Resize from 800x800 to 400x400, eg. resize down
+ mBaseBounds = new Rect(100, 100, 500, 500);
+ mStartBounds = new Rect(200, 200, 1_000, 1_000);
+ mEndBounds = new Rect(mBaseBounds);
+ final int duration = 10;
+ final float delta = 0;
+ final float[] matrix = new float[9];
+ mPipResizeAnimator = new PipResizeAnimator(mMockContext, mTestLeash,
+ mMockStartTransaction, mMockFinishTransaction,
+ mBaseBounds, mStartBounds, mEndBounds,
+ duration, delta);
+ mPipResizeAnimator.setSurfaceControlTransactionFactory(mMockFactory);
+
+ mPipResizeAnimator.setAnimationStartCallback(mMockStartCallback);
+ mPipResizeAnimator.setAnimationEndCallback(mMockEndCallback);
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+ mPipResizeAnimator.start();
+ clearInvocations(mMockTransaction);
+ mPipResizeAnimator.end();
+ });
+
+ // Start transaction scales down from its final state
+ verify(mMockStartTransaction).setMatrix(eq(mTestLeash), mArgumentCaptor.capture(), any());
+ mArgumentCaptor.getValue().getValues(matrix);
+ assertEquals(matrix[Matrix.MSCALE_X],
+ mStartBounds.width() / (float) mEndBounds.width(), FLOAT_COMPARISON_DELTA);
+ assertEquals(matrix[Matrix.MSCALE_Y],
+ mStartBounds.height() / (float) mEndBounds.height(), FLOAT_COMPARISON_DELTA);
+ assertEquals(matrix[Matrix.MTRANS_X], mStartBounds.left, FLOAT_COMPARISON_DELTA);
+ assertEquals(matrix[Matrix.MTRANS_Y], mStartBounds.top, FLOAT_COMPARISON_DELTA);
+
+ // Final animation transaction scales to 1 and puts the leash at final position
+ verify(mMockTransaction).setMatrix(eq(mTestLeash), mArgumentCaptor.capture(), any());
+ mArgumentCaptor.getValue().getValues(matrix);
+ assertEquals(matrix[Matrix.MSCALE_X], 1f, FLOAT_COMPARISON_DELTA);
+ assertEquals(matrix[Matrix.MSCALE_Y], 1f, FLOAT_COMPARISON_DELTA);
+ assertEquals(matrix[Matrix.MTRANS_X], mEndBounds.left, FLOAT_COMPARISON_DELTA);
+ assertEquals(matrix[Matrix.MTRANS_Y], mEndBounds.top, FLOAT_COMPARISON_DELTA);
+
+ // Finish transaction resets scale and puts the leash at final position
+ verify(mMockFinishTransaction).setMatrix(eq(mTestLeash), mArgumentCaptor.capture(), any());
+ mArgumentCaptor.getValue().getValues(matrix);
+ assertEquals(matrix[Matrix.MSCALE_X], 1f, FLOAT_COMPARISON_DELTA);
+ assertEquals(matrix[Matrix.MSCALE_Y], 1f, FLOAT_COMPARISON_DELTA);
+ assertEquals(matrix[Matrix.MTRANS_X], mEndBounds.left, FLOAT_COMPARISON_DELTA);
+ assertEquals(matrix[Matrix.MTRANS_Y], mEndBounds.top, FLOAT_COMPARISON_DELTA);
+ }
+
+ @Test
+ public void onAnimationEnd_resizeUp_sizeChanged() {
+ // Resize from 400x400 to 800x800, eg. resize up
+ mBaseBounds = new Rect(200, 200, 1_000, 1_000);
+ mStartBounds = new Rect(100, 100, 500, 500);
+ mEndBounds = new Rect(mBaseBounds);
+ final int duration = 10;
+ final float delta = 0;
+ final float[] matrix = new float[9];
+ mPipResizeAnimator = new PipResizeAnimator(mMockContext, mTestLeash,
+ mMockStartTransaction, mMockFinishTransaction,
+ mBaseBounds, mStartBounds, mEndBounds,
+ duration, delta);
+ mPipResizeAnimator.setSurfaceControlTransactionFactory(mMockFactory);
+
+ mPipResizeAnimator.setAnimationStartCallback(mMockStartCallback);
+ mPipResizeAnimator.setAnimationEndCallback(mMockEndCallback);
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+ mPipResizeAnimator.start();
+ clearInvocations(mMockTransaction);
+ mPipResizeAnimator.end();
+ });
+
+ // Start transaction scales up from its final state
+ verify(mMockStartTransaction).setMatrix(eq(mTestLeash), mArgumentCaptor.capture(), any());
+ mArgumentCaptor.getValue().getValues(matrix);
+ assertEquals(matrix[Matrix.MSCALE_X],
+ mStartBounds.width() / (float) mEndBounds.width(), FLOAT_COMPARISON_DELTA);
+ assertEquals(matrix[Matrix.MSCALE_Y],
+ mStartBounds.height() / (float) mEndBounds.height(), FLOAT_COMPARISON_DELTA);
+ assertEquals(matrix[Matrix.MTRANS_X], mStartBounds.left, FLOAT_COMPARISON_DELTA);
+ assertEquals(matrix[Matrix.MTRANS_Y], mStartBounds.top, FLOAT_COMPARISON_DELTA);
+
+ // Final animation transaction scales to 1 and puts the leash at final position
+ verify(mMockTransaction).setMatrix(eq(mTestLeash), mArgumentCaptor.capture(), any());
+ mArgumentCaptor.getValue().getValues(matrix);
+ assertEquals(matrix[Matrix.MSCALE_X], 1f, FLOAT_COMPARISON_DELTA);
+ assertEquals(matrix[Matrix.MSCALE_Y], 1f, FLOAT_COMPARISON_DELTA);
+ assertEquals(matrix[Matrix.MTRANS_X], mEndBounds.left, FLOAT_COMPARISON_DELTA);
+ assertEquals(matrix[Matrix.MTRANS_Y], mEndBounds.top, FLOAT_COMPARISON_DELTA);
+
+ // Finish transaction resets scale and puts the leash at final position
+ verify(mMockFinishTransaction).setMatrix(eq(mTestLeash), mArgumentCaptor.capture(), any());
+ mArgumentCaptor.getValue().getValues(matrix);
+ assertEquals(matrix[Matrix.MSCALE_X], 1f, FLOAT_COMPARISON_DELTA);
+ assertEquals(matrix[Matrix.MSCALE_Y], 1f, FLOAT_COMPARISON_DELTA);
+ assertEquals(matrix[Matrix.MTRANS_X], mEndBounds.left, FLOAT_COMPARISON_DELTA);
+ assertEquals(matrix[Matrix.MTRANS_Y], mEndBounds.top, FLOAT_COMPARISON_DELTA);
+ }
+
+ @Test
+ public void onAnimationEnd_withInitialDelta_rotateToZeroDegree() {
+ mBaseBounds = new Rect(200, 200, 1_000, 1_000);
+ mStartBounds = new Rect(100, 100, 500, 500);
+ mEndBounds = new Rect(mBaseBounds);
+ final int duration = 10;
+ final float delta = 45;
+ final float[] matrix = new float[9];
+ mPipResizeAnimator = new PipResizeAnimator(mMockContext, mTestLeash,
+ mMockStartTransaction, mMockFinishTransaction,
+ mBaseBounds, mStartBounds, mEndBounds,
+ duration, delta);
+ mPipResizeAnimator.setSurfaceControlTransactionFactory(mMockFactory);
+
+ mPipResizeAnimator.setAnimationStartCallback(mMockStartCallback);
+ mPipResizeAnimator.setAnimationEndCallback(mMockEndCallback);
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+ mPipResizeAnimator.start();
+ clearInvocations(mMockTransaction);
+ mPipResizeAnimator.end();
+ });
+
+ // Final animation transaction sets skew to zero
+ verify(mMockTransaction).setMatrix(eq(mTestLeash), mArgumentCaptor.capture(), any());
+ mArgumentCaptor.getValue().getValues(matrix);
+ assertEquals(matrix[Matrix.MSKEW_X], 0f, FLOAT_COMPARISON_DELTA);
+ assertEquals(matrix[Matrix.MSKEW_Y], 0f, FLOAT_COMPARISON_DELTA);
+
+ // Finish transaction sets skew to zero
+ verify(mMockFinishTransaction).setMatrix(eq(mTestLeash), mArgumentCaptor.capture(), any());
+ mArgumentCaptor.getValue().getValues(matrix);
+ assertEquals(matrix[Matrix.MSKEW_X], 0f, FLOAT_COMPARISON_DELTA);
+ assertEquals(matrix[Matrix.MSKEW_Y], 0f, FLOAT_COMPARISON_DELTA);
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/TaskStackTransitionObserverTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/TaskStackTransitionObserverTest.kt
index afdb68776d04..efe4fb18f273 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/TaskStackTransitionObserverTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/TaskStackTransitionObserverTest.kt
@@ -34,6 +34,7 @@ import com.android.wm.shell.common.ShellExecutor
import com.android.wm.shell.sysui.ShellInit
import com.android.wm.shell.transition.TransitionInfoBuilder
import com.android.wm.shell.transition.Transitions
+import com.android.wm.shell.windowdecor.extension.isFullscreen
import com.google.common.truth.Truth.assertThat
import dagger.Lazy
import org.junit.Before
@@ -107,8 +108,8 @@ class TaskStackTransitionObserverTest {
callOnTransitionFinished()
executor.flushAll()
- assertThat(listener.taskInfoToBeNotified.taskId).isEqualTo(change.taskInfo?.taskId)
- assertThat(listener.taskInfoToBeNotified.windowingMode)
+ assertThat(listener.taskInfoOnTaskMovedToFront.taskId).isEqualTo(change.taskInfo?.taskId)
+ assertThat(listener.taskInfoOnTaskMovedToFront.windowingMode)
.isEqualTo(change.taskInfo?.windowingMode)
}
@@ -130,8 +131,8 @@ class TaskStackTransitionObserverTest {
callOnTransitionFinished()
executor.flushAll()
- assertThat(listener.taskInfoToBeNotified.taskId).isEqualTo(1)
- assertThat(listener.taskInfoToBeNotified.windowingMode)
+ assertThat(listener.taskInfoOnTaskMovedToFront.taskId).isEqualTo(1)
+ assertThat(listener.taskInfoOnTaskMovedToFront.windowingMode)
.isEqualTo(WindowConfiguration.WINDOWING_MODE_FULLSCREEN)
}
@@ -161,9 +162,9 @@ class TaskStackTransitionObserverTest {
callOnTransitionFinished()
executor.flushAll()
- assertThat(listener.taskInfoToBeNotified.taskId)
+ assertThat(listener.taskInfoOnTaskMovedToFront.taskId)
.isEqualTo(freeformOpenChange.taskInfo?.taskId)
- assertThat(listener.taskInfoToBeNotified.windowingMode)
+ assertThat(listener.taskInfoOnTaskMovedToFront.windowingMode)
.isEqualTo(freeformOpenChange.taskInfo?.windowingMode)
}
@@ -199,9 +200,15 @@ class TaskStackTransitionObserverTest {
callOnTransitionFinished()
executor.flushAll()
- assertThat(listener.taskInfoToBeNotified.taskId).isEqualTo(change.taskInfo?.taskId)
- assertThat(listener.taskInfoToBeNotified.windowingMode)
+ assertThat(listener.taskInfoOnTaskMovedToFront.taskId).isEqualTo(change.taskInfo?.taskId)
+ assertThat(listener.taskInfoOnTaskMovedToFront.windowingMode)
.isEqualTo(change.taskInfo?.windowingMode)
+
+ assertThat(listener.taskInfoOnTaskChanged.size).isEqualTo(1)
+ with(listener.taskInfoOnTaskChanged.last()) {
+ assertThat(taskId).isEqualTo(mergedChange.taskInfo?.taskId)
+ assertThat(windowingMode).isEqualTo(mergedChange.taskInfo?.windowingMode)
+ }
}
@Test
@@ -236,18 +243,151 @@ class TaskStackTransitionObserverTest {
callOnTransitionFinished()
executor.flushAll()
- assertThat(listener.taskInfoToBeNotified.taskId).isEqualTo(mergedChange.taskInfo?.taskId)
- assertThat(listener.taskInfoToBeNotified.windowingMode)
- .isEqualTo(mergedChange.taskInfo?.windowingMode)
+ assertThat(listener.taskInfoOnTaskMovedToFront.taskId)
+ .isEqualTo(mergedChange.taskInfo?.taskId)
+ assertThat(listener.taskInfoOnTaskMovedToFront.windowingMode)
+ .isEqualTo(mergedChange.taskInfo?.windowingMode)
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_TASK_STACK_OBSERVER_IN_SHELL)
+ fun taskChange_freeformWindowToFullscreenWindow_listenerNotified() {
+ val listener = TestListener()
+ val executor = TestShellExecutor()
+ transitionObserver.addTaskStackTransitionObserverListener(listener, executor)
+ val freeformState =
+ createChange(
+ WindowManager.TRANSIT_OPEN,
+ createTaskInfo(1, WindowConfiguration.WINDOWING_MODE_FREEFORM)
+ )
+ val transitionInfoOpen =
+ TransitionInfoBuilder(WindowManager.TRANSIT_OPEN, 0).addChange(freeformState).build()
+ callOnTransitionReady(transitionInfoOpen)
+ callOnTransitionFinished()
+ executor.flushAll()
+
+ assertThat(listener.taskInfoOnTaskMovedToFront.taskId)
+ .isEqualTo(freeformState.taskInfo?.taskId)
+ assertThat(listener.taskInfoOnTaskMovedToFront.windowingMode)
+ .isEqualTo(freeformState.taskInfo?.windowingMode)
+ assertThat(listener.taskInfoOnTaskMovedToFront.isFullscreen).isEqualTo(false)
+
+ // create change transition to update the windowing mode to full screen.
+ val fullscreenState =
+ createChange(
+ WindowManager.TRANSIT_CHANGE,
+ createTaskInfo(1, WindowConfiguration.WINDOWING_MODE_FULLSCREEN)
+ )
+ val transitionInfoChange =
+ TransitionInfoBuilder(WindowManager.TRANSIT_CHANGE, 0)
+ .addChange(fullscreenState)
+ .build()
+
+ callOnTransitionReady(transitionInfoChange)
+ callOnTransitionFinished()
+ executor.flushAll()
+
+ // Asserting whether freeformState remains the same as before the change
+ assertThat(listener.taskInfoOnTaskMovedToFront.taskId)
+ .isEqualTo(freeformState.taskInfo?.taskId)
+ assertThat(listener.taskInfoOnTaskMovedToFront.isFullscreen).isEqualTo(false)
+
+ // Asserting changes
+ assertThat(listener.taskInfoOnTaskChanged.size).isEqualTo(1)
+ with(listener.taskInfoOnTaskChanged.last()) {
+ assertThat(taskId).isEqualTo(fullscreenState.taskInfo?.taskId)
+ assertThat(isFullscreen).isEqualTo(true)
+ }
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_TASK_STACK_OBSERVER_IN_SHELL)
+ fun singleTransition_withOpenAndChange_onlyOpenIsNotified() {
+ val listener = TestListener()
+ val executor = TestShellExecutor()
+ transitionObserver.addTaskStackTransitionObserverListener(listener, executor)
+
+ // Creating multiple changes to be fired in a single transition
+ val freeformState =
+ createChange(
+ mode = WindowManager.TRANSIT_OPEN,
+ taskInfo = createTaskInfo(1, WindowConfiguration.WINDOWING_MODE_FREEFORM)
+ )
+ val fullscreenState =
+ createChange(
+ mode = WindowManager.TRANSIT_CHANGE,
+ taskInfo = createTaskInfo(1, WindowConfiguration.WINDOWING_MODE_FULLSCREEN)
+ )
+
+ val transitionInfoWithChanges =
+ TransitionInfoBuilder(WindowManager.TRANSIT_CHANGE, 0)
+ .addChange(freeformState)
+ .addChange(fullscreenState)
+ .build()
+
+ callOnTransitionReady(transitionInfoWithChanges)
+ callOnTransitionFinished()
+ executor.flushAll()
+
+ assertThat(listener.taskInfoOnTaskMovedToFront.taskId)
+ .isEqualTo(freeformState.taskInfo?.taskId)
+ assertThat(listener.taskInfoOnTaskMovedToFront.isFullscreen).isEqualTo(false)
+ assertThat(listener.taskInfoOnTaskChanged.size).isEqualTo(0)
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_TASK_STACK_OBSERVER_IN_SHELL)
+ fun singleTransition_withMultipleChanges_listenerNotified_forEachChange() {
+ val listener = TestListener()
+ val executor = TestShellExecutor()
+ transitionObserver.addTaskStackTransitionObserverListener(listener, executor)
+
+ val taskId = 1
+
+ // Creating multiple changes to be fired in a single transition
+ val changes =
+ listOf(
+ WindowConfiguration.WINDOWING_MODE_FREEFORM,
+ WindowConfiguration.WINDOW_CONFIG_DISPLAY_ROTATION,
+ WindowConfiguration.WINDOWING_MODE_FULLSCREEN
+ )
+ .map { change ->
+ createChange(
+ mode = WindowManager.TRANSIT_CHANGE,
+ taskInfo = createTaskInfo(taskId, change)
+ )
+ }
+
+ val transitionInfoWithChanges =
+ TransitionInfoBuilder(WindowManager.TRANSIT_CHANGE, 0)
+ .apply { changes.forEach { c -> this@apply.addChange(c) } }
+ .build()
+
+ callOnTransitionReady(transitionInfoWithChanges)
+ callOnTransitionFinished()
+ executor.flushAll()
+
+ assertThat(listener.taskInfoOnTaskChanged.size).isEqualTo(changes.size)
+ changes.forEachIndexed { index, change ->
+ assertThat(listener.taskInfoOnTaskChanged[index].taskId)
+ .isEqualTo(change.taskInfo?.taskId)
+ assertThat(listener.taskInfoOnTaskChanged[index].windowingMode)
+ .isEqualTo(change.taskInfo?.windowingMode)
+ }
}
class TestListener : TaskStackTransitionObserver.TaskStackTransitionObserverListener {
- var taskInfoToBeNotified = ActivityManager.RunningTaskInfo()
+ var taskInfoOnTaskMovedToFront = ActivityManager.RunningTaskInfo()
+ var taskInfoOnTaskChanged = mutableListOf<ActivityManager.RunningTaskInfo>()
override fun onTaskMovedToFrontThroughTransition(
taskInfo: ActivityManager.RunningTaskInfo
) {
- taskInfoToBeNotified = taskInfo
+ taskInfoOnTaskMovedToFront = taskInfo
+ }
+
+ override fun onTaskChangedThroughTransition(taskInfo: ActivityManager.RunningTaskInfo) {
+ taskInfoOnTaskChanged += taskInfo
}
}
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 c42be7fef2a6..36c5be1d7191 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
@@ -946,7 +946,7 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() {
}
@Test
- fun testDecor_onClickToSplitScreen_detachesStatusBarInputLayer() {
+ fun testDecor_onClickToSplitScreen_disposesStatusBarInputLayer() {
val toSplitScreenListenerCaptor = forClass(Function0::class.java)
as ArgumentCaptor<Function0<Unit>>
val decor = createOpenTaskDecoration(
@@ -956,7 +956,7 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() {
toSplitScreenListenerCaptor.value.invoke()
- verify(decor).detachStatusBarInputLayer()
+ verify(decor).disposeStatusBarInputLayer()
}
@Test
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 8a2c7782906d..f6fed29bbbe8 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
@@ -49,6 +49,7 @@ 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;
@@ -849,7 +850,8 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
ArgumentCaptor<Runnable> runnableArgument = ArgumentCaptor.forClass(Runnable.class);
spyWindowDecor.relayout(taskInfo, true /* hasGlobalFocus */);
- verify(mMockHandler).post(runnableArgument.capture());
+ // 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());
}
@@ -876,7 +878,8 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
ArgumentCaptor<Runnable> runnableArgument = ArgumentCaptor.forClass(Runnable.class);
spyWindowDecor.relayout(taskInfo, true /* hasGlobalFocus */);
- verify(mMockHandler).post(runnableArgument.capture());
+ // Once for view host, the other for the AppHandle input layer.
+ verify(mMockHandler, times(2)).post(runnableArgument.capture());
spyWindowDecor.relayout(taskInfo, true /* hasGlobalFocus */);
@@ -890,7 +893,8 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
ArgumentCaptor<Runnable> runnableArgument = ArgumentCaptor.forClass(Runnable.class);
spyWindowDecor.relayout(taskInfo, true /* hasGlobalFocus */);
- verify(mMockHandler).post(runnableArgument.capture());
+ // Once for view host, the other for the AppHandle input layer.
+ verify(mMockHandler, times(2)).post(runnableArgument.capture());
spyWindowDecor.close();
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 cb7fadee9822..8e0434cb28f7 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
@@ -218,8 +218,6 @@ public class WindowDecorationTests extends ShellTestCase {
verify(captionContainerSurfaceBuilder, never()).build();
verify(mMockSurfaceControlViewHostFactory, never()).create(any(), any(), any());
- verify(mMockSurfaceControlFinishT).hide(mMockTaskSurface);
-
assertNull(mRelayoutResult.mRootView);
}
@@ -281,8 +279,6 @@ public class WindowDecorationTests extends ShellTestCase {
verify(mMockSurfaceControlStartT).setCornerRadius(mMockTaskSurface, CORNER_RADIUS);
verify(mMockSurfaceControlFinishT).setCornerRadius(mMockTaskSurface, CORNER_RADIUS);
- verify(mMockSurfaceControlStartT)
- .show(mMockTaskSurface);
verify(mMockSurfaceControlStartT).setShadowRadius(mMockTaskSurface, 10);
assertEquals(300, mRelayoutResult.mWidth);
@@ -863,7 +859,7 @@ public class WindowDecorationTests extends ShellTestCase {
final TestWindowDecoration windowDecor = createWindowDecoration(taskInfo);
- mRelayoutParams.mSetTaskPositionAndCrop = false;
+ mRelayoutParams.mSetTaskVisibilityPositionAndCrop = false;
windowDecor.relayout(taskInfo, true /* hasGlobalFocus */);
verify(mMockSurfaceControlStartT, never()).setWindowCrop(
@@ -891,7 +887,7 @@ public class WindowDecorationTests extends ShellTestCase {
taskInfo.configuration.densityDpi = DisplayMetrics.DENSITY_DEFAULT * 2;
final TestWindowDecoration windowDecor = createWindowDecoration(taskInfo);
- mRelayoutParams.mSetTaskPositionAndCrop = true;
+ mRelayoutParams.mSetTaskVisibilityPositionAndCrop = true;
windowDecor.relayout(taskInfo, true /* hasGlobalFocus */);
verify(mMockSurfaceControlStartT).setWindowCrop(
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 52e93bb87ea3..80ad1df44a1b 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
@@ -36,6 +36,7 @@ import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.kotlin.any
+import org.mockito.kotlin.eq
import org.mockito.kotlin.mock
import org.mockito.kotlin.times
import org.mockito.kotlin.verify
@@ -145,7 +146,7 @@ class DesktopTilingDecorViewModelTest : ShellTestCase() {
}
@Test
- fun userChange_starting_allTilingSessionsShouldBeDestroyed() {
+ fun onUserChange_allTilingSessionsShouldBeDestroyed() {
desktopTilingDecorViewModel.tilingTransitionHandlerByDisplayId.put(
1,
desktopTilingDecoration,
@@ -157,7 +158,29 @@ class DesktopTilingDecorViewModelTest : ShellTestCase() {
desktopTilingDecorViewModel.onUserChange()
- verify(desktopTilingDecoration, times(2)).onUserChange()
+ verify(desktopTilingDecoration, times(2)).resetTilingSession()
+ }
+
+ @Test
+ fun displayOrientationChange_tilingForDisplayShouldBeDestroyed() {
+ desktopTilingDecorViewModel.tilingTransitionHandlerByDisplayId.put(
+ 1,
+ desktopTilingDecoration,
+ )
+ desktopTilingDecorViewModel.tilingTransitionHandlerByDisplayId.put(
+ 2,
+ desktopTilingDecoration,
+ )
+
+ desktopTilingDecorViewModel.onDisplayChange(1, 1, 2, null, null)
+
+ verify(desktopTilingDecoration, times(1)).resetTilingSession()
+ verify(displayControllerMock, times(1))
+ .addDisplayChangingController(eq(desktopTilingDecorViewModel))
+
+ desktopTilingDecorViewModel.onDisplayChange(1, 1, 3, null, null)
+ // No extra calls after 180 degree change.
+ verify(desktopTilingDecoration, times(1)).resetTilingSession()
}
companion object {
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDividerWindowManagerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDividerWindowManagerTest.kt
index 0ee3f4695e85..3143946fa828 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDividerWindowManagerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDividerWindowManagerTest.kt
@@ -16,9 +16,12 @@
package com.android.wm.shell.windowdecor.tiling
+import android.content.Context
import android.content.res.Configuration
import android.graphics.Rect
import android.testing.AndroidTestingRunner
+import android.view.Display
+import android.view.RoundedCorner
import android.view.SurfaceControl
import androidx.test.annotation.UiThreadTest
import androidx.test.filters.SmallTest
@@ -29,6 +32,7 @@ import kotlin.test.Test
import org.junit.Before
import org.junit.runner.RunWith
import org.mockito.kotlin.any
+import org.mockito.kotlin.eq
import org.mockito.kotlin.mock
import org.mockito.kotlin.times
import org.mockito.kotlin.verify
@@ -55,10 +59,17 @@ class DesktopTilingDividerWindowManagerTest : ShellTestCase() {
private lateinit var desktopTilingWindowManager: DesktopTilingDividerWindowManager
+ private val context = mock<Context>()
+ private val display = mock<Display>()
+ private val roundedCorner = mock<RoundedCorner>()
+
@Before
fun setup() {
config = Configuration()
config.setToDefaults()
+ whenever(context.display).thenReturn(display)
+ whenever(display.getRoundedCorner(any())).thenReturn(roundedCorner)
+ whenever(roundedCorner.radius).thenReturn(CORNER_RADIUS)
desktopTilingWindowManager =
DesktopTilingDividerWindowManager(
config,
@@ -69,6 +80,7 @@ class DesktopTilingDividerWindowManagerTest : ShellTestCase() {
transitionHandlerMock,
transactionSupplierMock,
BOUNDS,
+ context,
)
}
@@ -85,7 +97,6 @@ class DesktopTilingDividerWindowManagerTest : ShellTestCase() {
// Ensure a surfaceControl transaction runs to show the divider.
verify(transactionSupplierMock, times(1)).get()
- verify(syncQueueMock, times(1)).runInSync(any())
desktopTilingWindowManager.release()
verify(transaction, times(1)).hide(any())
@@ -93,7 +104,24 @@ class DesktopTilingDividerWindowManagerTest : ShellTestCase() {
verify(transaction, times(1)).apply()
}
+ @Test
+ @UiThreadTest
+ fun testWindowManager_accountsForRoundedCornerDimensions() {
+ whenever(transactionSupplierMock.get()).thenReturn(transaction)
+ whenever(transaction.setRelativeLayer(any(), any(), any())).thenReturn(transaction)
+ whenever(transaction.setRelativeLayer(any(), any(), any())).thenReturn(transaction)
+ whenever(transaction.setPosition(any(), any(), any())).thenReturn(transaction)
+ whenever(transaction.show(any())).thenReturn(transaction)
+
+ desktopTilingWindowManager.generateViewHost(surfaceControl)
+
+ // Ensure a surfaceControl transaction runs to show the divider.
+ verify(transaction, times(1))
+ .setPosition(any(), eq(BOUNDS.left.toFloat() - CORNER_RADIUS), any())
+ }
+
companion object {
private val BOUNDS = Rect(1, 2, 3, 4)
+ private val CORNER_RADIUS = 28
}
}
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 2ae2461c8f69..f371f5223419 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
@@ -475,7 +475,7 @@ class DesktopTilingWindowDecorationTest : ShellTestCase() {
tilingDecoration.rightTaskResizingHelper = tiledTaskHelper
tilingDecoration.desktopTilingDividerWindowManager = desktopTilingDividerWindowManager
- tilingDecoration.onUserChange()
+ tilingDecoration.resetTilingSession()
assertThat(tilingDecoration.leftTaskResizingHelper).isNull()
assertThat(tilingDecoration.rightTaskResizingHelper).isNull()
diff --git a/libs/appfunctions/api/current.txt b/libs/appfunctions/api/current.txt
index 27817e9eb984..faf84a8ab5ac 100644
--- a/libs/appfunctions/api/current.txt
+++ b/libs/appfunctions/api/current.txt
@@ -3,8 +3,9 @@ package com.google.android.appfunctions.sidecar {
public final class AppFunctionManager {
ctor public AppFunctionManager(android.content.Context);
- method public void executeAppFunction(@NonNull com.google.android.appfunctions.sidecar.ExecuteAppFunctionRequest, @NonNull java.util.concurrent.Executor, @NonNull android.os.CancellationSignal, @NonNull java.util.function.Consumer<com.google.android.appfunctions.sidecar.ExecuteAppFunctionResponse>);
- method 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.Manifest.permission.EXECUTE_APP_FUNCTIONS_TRUSTED, android.Manifest.permission.EXECUTE_APP_FUNCTIONS}, conditional=true) public void executeAppFunction(@NonNull com.google.android.appfunctions.sidecar.ExecuteAppFunctionRequest, @NonNull java.util.concurrent.Executor, @NonNull android.os.CancellationSignal, @NonNull java.util.function.Consumer<com.google.android.appfunctions.sidecar.ExecuteAppFunctionResponse>);
+ method @RequiresPermission(anyOf={android.Manifest.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
field public static final int APP_FUNCTION_STATE_DISABLED = 2; // 0x2
@@ -34,6 +35,7 @@ package com.google.android.appfunctions.sidecar {
}
public final class ExecuteAppFunctionResponse {
+ method public int getErrorCategory();
method @Nullable public String getErrorMessage();
method @NonNull public android.os.Bundle getExtras();
method public int getResultCode();
@@ -41,14 +43,19 @@ package com.google.android.appfunctions.sidecar {
method public boolean isSuccess();
method @NonNull public static com.google.android.appfunctions.sidecar.ExecuteAppFunctionResponse newFailure(int, @Nullable String, @Nullable android.os.Bundle);
method @NonNull public static com.google.android.appfunctions.sidecar.ExecuteAppFunctionResponse newSuccess(@NonNull android.app.appsearch.GenericDocument, @Nullable android.os.Bundle);
+ field public static final int ERROR_CATEGORY_APP = 3; // 0x3
+ field public static final int ERROR_CATEGORY_REQUEST_ERROR = 1; // 0x1
+ field public static final int ERROR_CATEGORY_SYSTEM = 2; // 0x2
+ field public static final int ERROR_CATEGORY_UNKNOWN = 0; // 0x0
field public static final String PROPERTY_RETURN_VALUE = "returnValue";
- field public static final int RESULT_APP_UNKNOWN_ERROR = 2; // 0x2
- field public static final int RESULT_CANCELLED = 6; // 0x6
- field public static final int RESULT_DENIED = 1; // 0x1
- field public static final int RESULT_DISABLED = 5; // 0x5
- field public static final int RESULT_INTERNAL_ERROR = 3; // 0x3
- field public static final int RESULT_INVALID_ARGUMENT = 4; // 0x4
+ field public static final int RESULT_APP_UNKNOWN_ERROR = 3000; // 0xbb8
+ field public static final int RESULT_CANCELLED = 2001; // 0x7d1
+ field public static final int RESULT_DENIED = 1000; // 0x3e8
+ field public static final int RESULT_DISABLED = 1002; // 0x3ea
+ field public static final int RESULT_FUNCTION_NOT_FOUND = 1003; // 0x3eb
+ field public static final int RESULT_INVALID_ARGUMENT = 1001; // 0x3e9
field public static final int RESULT_OK = 0; // 0x0
+ field public static final int RESULT_SYSTEM_ERROR = 2000; // 0x7d0
}
}
diff --git a/libs/appfunctions/java/com/google/android/appfunctions/sidecar/AppFunctionManager.java b/libs/appfunctions/java/com/google/android/appfunctions/sidecar/AppFunctionManager.java
index 6870446fc3a0..2075104ff868 100644
--- a/libs/appfunctions/java/com/google/android/appfunctions/sidecar/AppFunctionManager.java
+++ b/libs/appfunctions/java/com/google/android/appfunctions/sidecar/AppFunctionManager.java
@@ -16,9 +16,11 @@
package com.google.android.appfunctions.sidecar;
+import android.Manifest;
import android.annotation.CallbackExecutor;
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
import android.annotation.SuppressLint;
import android.annotation.UserHandleAware;
import android.content.Context;
@@ -103,6 +105,12 @@ public final class AppFunctionManager {
* <p>See {@link android.app.appfunctions.AppFunctionManager#executeAppFunction} for the
* documented behaviour of this method.
*/
+ @RequiresPermission(
+ anyOf = {
+ Manifest.permission.EXECUTE_APP_FUNCTIONS_TRUSTED,
+ Manifest.permission.EXECUTE_APP_FUNCTIONS
+ },
+ conditional = true)
public void executeAppFunction(
@NonNull ExecuteAppFunctionRequest sidecarRequest,
@NonNull @CallbackExecutor Executor executor,
@@ -131,6 +139,12 @@ public final class AppFunctionManager {
* <p>See {@link android.app.appfunctions.AppFunctionManager#isAppFunctionEnabled} for the
* documented behaviour of this method.
*/
+ @RequiresPermission(
+ anyOf = {
+ Manifest.permission.EXECUTE_APP_FUNCTIONS_TRUSTED,
+ Manifest.permission.EXECUTE_APP_FUNCTIONS
+ },
+ conditional = true)
public void isAppFunctionEnabled(
@NonNull String functionIdentifier,
@NonNull String targetPackage,
@@ -140,6 +154,19 @@ public final class AppFunctionManager {
}
/**
+ * Returns a boolean through a callback, indicating whether the app function is enabled.
+ *
+ * <p>See {@link android.app.appfunctions.AppFunctionManager#isAppFunctionEnabled} for the
+ * documented behaviour of this method.
+ */
+ public void isAppFunctionEnabled(
+ @NonNull String functionIdentifier,
+ @NonNull Executor executor,
+ @NonNull OutcomeReceiver<Boolean, Exception> callback) {
+ mManager.isAppFunctionEnabled(functionIdentifier, executor, callback);
+ }
+
+ /**
* Sets the enabled state of the app function owned by the calling package.
*
* <p>See {@link android.app.appfunctions.AppFunctionManager#setAppFunctionEnabled} for the
diff --git a/libs/appfunctions/java/com/google/android/appfunctions/sidecar/ExecuteAppFunctionResponse.java b/libs/appfunctions/java/com/google/android/appfunctions/sidecar/ExecuteAppFunctionResponse.java
index d5dfaeb77140..4e88fb025a9d 100644
--- a/libs/appfunctions/java/com/google/android/appfunctions/sidecar/ExecuteAppFunctionResponse.java
+++ b/libs/appfunctions/java/com/google/android/appfunctions/sidecar/ExecuteAppFunctionResponse.java
@@ -50,38 +50,102 @@ public final class ExecuteAppFunctionResponse {
*/
public static final String PROPERTY_RETURN_VALUE = "returnValue";
- /** The call was successful. */
+ /**
+ * The call was successful.
+ *
+ * <p>This result code does not belong in an error category.
+ */
public static final int RESULT_OK = 0;
- /** The caller does not have the permission to execute an app function. */
- public static final int RESULT_DENIED = 1;
+ /**
+ * The caller does not have the permission to execute an app function.
+ *
+ * <p>This error is in the {@link #ERROR_CATEGORY_REQUEST_ERROR} category.
+ */
+ public static final int RESULT_DENIED = 1000;
+
+ /**
+ * The caller supplied invalid arguments to the execution request.
+ *
+ * <p>This error may be considered similar to {@link IllegalArgumentException}.
+ *
+ * <p>This error is in the {@link #ERROR_CATEGORY_REQUEST_ERROR} category.
+ */
+ public static final int RESULT_INVALID_ARGUMENT = 1001;
+
+ /**
+ * The caller tried to execute a disabled app function.
+ *
+ * <p>This error is in the {@link #ERROR_CATEGORY_REQUEST_ERROR} category.
+ */
+ public static final int RESULT_DISABLED = 1002;
+
+ /**
+ * The caller tried to execute a function that does not exist.
+ *
+ * <p>This error is in the {@link #ERROR_CATEGORY_REQUEST_ERROR} category.
+ */
+ public static final int RESULT_FUNCTION_NOT_FOUND = 1003;
+
+ /**
+ * An internal unexpected error coming from the system.
+ *
+ * <p>This error is in the {@link #ERROR_CATEGORY_SYSTEM} category.
+ */
+ public static final int RESULT_SYSTEM_ERROR = 2000;
+
+ /**
+ * The operation was cancelled. Use this error code to report that a cancellation is done after
+ * receiving a cancellation signal.
+ *
+ * <p>This error is in the {@link #ERROR_CATEGORY_SYSTEM} category.
+ */
+ public static final int RESULT_CANCELLED = 2001;
/**
* 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
* unexpected error is thrown from the bound application.
+ *
+ * <p>This error is in the {@link #ERROR_CATEGORY_APP} category.
*/
- public static final int RESULT_APP_UNKNOWN_ERROR = 2;
+ public static final int RESULT_APP_UNKNOWN_ERROR = 3000;
- /** An internal unexpected error coming from the system. */
- public static final int RESULT_INTERNAL_ERROR = 3;
+ /**
+ * The error category is unknown.
+ *
+ * <p>This is the default value for {@link #getErrorCategory}.
+ */
+ public static final int ERROR_CATEGORY_UNKNOWN = 0;
/**
- * The caller supplied invalid arguments to the call.
+ * The error is caused by the app requesting a function execution.
*
- * <p>This error may be considered similar to {@link IllegalArgumentException}.
+ * <p>For example, the caller provided invalid parameters in the execution request e.g. an
+ * invalid function ID.
+ *
+ * <p>Errors in the category fall in the range 1000-1999 inclusive.
*/
- public static final int RESULT_INVALID_ARGUMENT = 4;
+ public static final int ERROR_CATEGORY_REQUEST_ERROR = 1;
- /** The caller tried to execute a disabled app function. */
- public static final int RESULT_DISABLED = 5;
+ /**
+ * The error is caused by an issue in the system.
+ *
+ * <p>For example, the AppFunctionService implementation is not found by the system.
+ *
+ * <p>Errors in the category fall in the range 2000-2999 inclusive.
+ */
+ public static final int ERROR_CATEGORY_SYSTEM = 2;
/**
- * The operation was cancelled. Use this error code to report that a cancellation is done after
- * receiving a cancellation signal.
+ * The error is caused by the app providing the function.
+ *
+ * <p>For example, the app crashed when the system is executing the request.
+ *
+ * <p>Errors in the category fall in the range 3000-3999 inclusive.
*/
- public static final int RESULT_CANCELLED = 6;
+ public static final int ERROR_CATEGORY_APP = 3;
/** The result code of the app function execution. */
@ResultCode private final int mResultCode;
@@ -171,6 +235,36 @@ public final class ExecuteAppFunctionResponse {
}
/**
+ * Returns the error category of the {@link ExecuteAppFunctionResponse}.
+ *
+ * <p>This method categorizes errors based on their underlying cause, allowing developers to
+ * implement targeted error handling and provide more informative error messages to users. It
+ * maps ranges of result codes to specific error categories.
+ *
+ * <p>When constructing a {@link #newFailure} response, use the appropriate result code value to
+ * ensure correct categorization of the failed response.
+ *
+ * <p>This method returns {@code ERROR_CATEGORY_UNKNOWN} if the result code does not belong to
+ * any error category, for example, in the case of a successful result with {@link #RESULT_OK}.
+ *
+ * <p>See {@link ErrorCategory} for a complete list of error categories and their corresponding
+ * result code ranges.
+ */
+ @ErrorCategory
+ public int getErrorCategory() {
+ if (mResultCode >= 1000 && mResultCode < 2000) {
+ return ERROR_CATEGORY_REQUEST_ERROR;
+ }
+ if (mResultCode >= 2000 && mResultCode < 3000) {
+ return ERROR_CATEGORY_SYSTEM;
+ }
+ if (mResultCode >= 3000 && mResultCode < 4000) {
+ return ERROR_CATEGORY_APP;
+ }
+ return ERROR_CATEGORY_UNKNOWN;
+ }
+
+ /**
* Returns a generic document containing the return value of the executed function.
*
* <p>The {@link #PROPERTY_RETURN_VALUE} key can be used to obtain the return value.
@@ -238,11 +332,28 @@ public final class ExecuteAppFunctionResponse {
RESULT_OK,
RESULT_DENIED,
RESULT_APP_UNKNOWN_ERROR,
- RESULT_INTERNAL_ERROR,
+ RESULT_SYSTEM_ERROR,
+ RESULT_FUNCTION_NOT_FOUND,
RESULT_INVALID_ARGUMENT,
RESULT_DISABLED,
RESULT_CANCELLED
})
@Retention(RetentionPolicy.SOURCE)
public @interface ResultCode {}
+
+ /**
+ * Error categories.
+ *
+ * @hide
+ */
+ @IntDef(
+ prefix = {"ERROR_CATEGORY_"},
+ value = {
+ ERROR_CATEGORY_UNKNOWN,
+ ERROR_CATEGORY_REQUEST_ERROR,
+ ERROR_CATEGORY_APP,
+ ERROR_CATEGORY_SYSTEM
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ErrorCategory {}
}
diff --git a/libs/appfunctions/tests/src/com/google/android/appfunctions/sidecar/tests/SidecarConverterTest.kt b/libs/appfunctions/tests/src/com/google/android/appfunctions/sidecar/tests/SidecarConverterTest.kt
index 1f9fddd3c1ec..264f84209caf 100644
--- a/libs/appfunctions/tests/src/com/google/android/appfunctions/sidecar/tests/SidecarConverterTest.kt
+++ b/libs/appfunctions/tests/src/com/google/android/appfunctions/sidecar/tests/SidecarConverterTest.kt
@@ -105,7 +105,7 @@ class SidecarConverterTest {
val emptyGd = GenericDocument.Builder<GenericDocument.Builder<*>>("", "", "").build()
val platformResponse =
ExecuteAppFunctionResponse.newFailure(
- ExecuteAppFunctionResponse.RESULT_INTERNAL_ERROR,
+ ExecuteAppFunctionResponse.RESULT_SYSTEM_ERROR,
null,
null
)
@@ -119,7 +119,7 @@ class SidecarConverterTest {
assertThat(sidecarResponse.resultDocument.id).isEqualTo(emptyGd.id)
assertThat(sidecarResponse.resultDocument.schemaType).isEqualTo(emptyGd.schemaType)
assertThat(sidecarResponse.resultCode)
- .isEqualTo(ExecuteAppFunctionResponse.RESULT_INTERNAL_ERROR)
+ .isEqualTo(ExecuteAppFunctionResponse.RESULT_SYSTEM_ERROR)
assertThat(sidecarResponse.errorMessage).isNull()
}
@@ -152,7 +152,7 @@ class SidecarConverterTest {
val emptyGd = GenericDocument.Builder<GenericDocument.Builder<*>>("", "", "").build()
val sidecarResponse =
com.google.android.appfunctions.sidecar.ExecuteAppFunctionResponse.newFailure(
- ExecuteAppFunctionResponse.RESULT_INTERNAL_ERROR,
+ ExecuteAppFunctionResponse.RESULT_SYSTEM_ERROR,
null,
null
)
@@ -166,7 +166,7 @@ class SidecarConverterTest {
assertThat(platformResponse.resultDocument.id).isEqualTo(emptyGd.id)
assertThat(platformResponse.resultDocument.schemaType).isEqualTo(emptyGd.schemaType)
assertThat(platformResponse.resultCode)
- .isEqualTo(ExecuteAppFunctionResponse.RESULT_INTERNAL_ERROR)
+ .isEqualTo(ExecuteAppFunctionResponse.RESULT_SYSTEM_ERROR)
assertThat(platformResponse.errorMessage).isNull()
}
}
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index 266c23631800..fcb7efc35c94 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -384,6 +384,7 @@ cc_defaults {
"jni/ScopedParcel.cpp",
"jni/Shader.cpp",
"jni/RenderEffect.cpp",
+ "jni/RuntimeEffectUtils.cpp",
"jni/Typeface.cpp",
"jni/Utils.cpp",
"jni/YuvToJpegEncoder.cpp",
@@ -579,6 +580,7 @@ cc_defaults {
"utils/Color.cpp",
"utils/LinearAllocator.cpp",
"utils/StringUtils.cpp",
+ "utils/StatsUtils.cpp",
"utils/TypefaceUtils.cpp",
"utils/VectorDrawableUtils.cpp",
"AnimationContext.cpp",
diff --git a/libs/hwui/ColorFilter.h b/libs/hwui/ColorFilter.h
index 3a3bfb47dfdd..f6b6be08997a 100644
--- a/libs/hwui/ColorFilter.h
+++ b/libs/hwui/ColorFilter.h
@@ -22,6 +22,7 @@
#include <memory>
#include "GraphicsJNI.h"
+#include "RuntimeEffectUtils.h"
#include "SkColorFilter.h"
namespace android {
@@ -113,6 +114,36 @@ private:
std::vector<float> mMatrix;
};
+class RuntimeColorFilter : public ColorFilter {
+public:
+ RuntimeColorFilter(SkRuntimeEffectBuilder* builder) : mBuilder(builder) {}
+
+ void updateUniforms(JNIEnv* env, const char* name, const float vals[], int count,
+ bool isColor) {
+ UpdateFloatUniforms(env, mBuilder, name, vals, count, isColor);
+ discardInstance();
+ }
+
+ void updateUniforms(JNIEnv* env, const char* name, const int vals[], int count) {
+ UpdateIntUniforms(env, mBuilder, name, vals, count);
+ discardInstance();
+ }
+
+ void updateChild(JNIEnv* env, const char* name, SkFlattenable* childEffect) {
+ UpdateChild(env, mBuilder, name, childEffect);
+ discardInstance();
+ }
+
+private:
+ sk_sp<SkColorFilter> createInstance() override {
+ // TODO: throw error if null
+ return mBuilder->makeColorFilter();
+ }
+
+private:
+ SkRuntimeEffectBuilder* mBuilder;
+};
+
} // namespace uirenderer
} // namespace android
diff --git a/libs/hwui/hwui/ImageDecoder.cpp b/libs/hwui/hwui/ImageDecoder.cpp
index e074a27db38f..a9a5db8181ba 100644
--- a/libs/hwui/hwui/ImageDecoder.cpp
+++ b/libs/hwui/hwui/ImageDecoder.cpp
@@ -27,8 +27,8 @@
#include <SkColorSpace.h>
#include <SkColorType.h>
#include <SkEncodedOrigin.h>
-#include <SkImageInfo.h>
#include <SkGainmapInfo.h>
+#include <SkImageInfo.h>
#include <SkMatrix.h>
#include <SkPaint.h>
#include <SkPngChunkReader.h>
@@ -43,6 +43,8 @@
#include <memory>
+#include "modules/skcms/src/skcms_public.h"
+
using namespace android;
sk_sp<SkColorSpace> ImageDecoder::getDefaultColorSpace() const {
diff --git a/libs/hwui/jni/BitmapFactory.cpp b/libs/hwui/jni/BitmapFactory.cpp
index 49a7f73fb3a3..8b43f1db84af 100644
--- a/libs/hwui/jni/BitmapFactory.cpp
+++ b/libs/hwui/jni/BitmapFactory.cpp
@@ -10,6 +10,7 @@
#include <stdint.h>
#include <stdio.h>
#include <sys/stat.h>
+#include <utils/StatsUtils.h>
#include <memory>
@@ -630,6 +631,7 @@ static jobject doDecode(JNIEnv* env, std::unique_ptr<SkStreamRewindable> stream,
}
bitmap::reinitBitmap(env, javaBitmap, outputBitmap.info(), isPremultiplied);
outputBitmap.notifyPixelsChanged();
+ uirenderer::logBitmapDecode(*reuseBitmap);
// If a java bitmap was passed in for reuse, pass it back
return javaBitmap;
}
@@ -650,6 +652,7 @@ static jobject doDecode(JNIEnv* env, std::unique_ptr<SkStreamRewindable> stream,
}
}
+ uirenderer::logBitmapDecode(*hardwareBitmap);
return bitmap::createBitmap(env, hardwareBitmap.release(), bitmapCreateFlags,
ninePatchChunk, ninePatchInsets, -1);
}
@@ -659,6 +662,7 @@ static jobject doDecode(JNIEnv* env, std::unique_ptr<SkStreamRewindable> stream,
heapBitmap->setGainmap(std::move(gainmap));
}
+ uirenderer::logBitmapDecode(*heapBitmap);
// now create the java bitmap
return bitmap::createBitmap(env, heapBitmap, bitmapCreateFlags, ninePatchChunk, ninePatchInsets,
-1);
diff --git a/libs/hwui/jni/BitmapRegionDecoder.cpp b/libs/hwui/jni/BitmapRegionDecoder.cpp
index f7e8e073a272..5ffd5b9016d8 100644
--- a/libs/hwui/jni/BitmapRegionDecoder.cpp
+++ b/libs/hwui/jni/BitmapRegionDecoder.cpp
@@ -19,6 +19,7 @@
#include <HardwareBitmapUploader.h>
#include <androidfw/Asset.h>
#include <sys/stat.h>
+#include <utils/StatsUtils.h>
#include <memory>
@@ -376,6 +377,7 @@ static jobject nativeDecodeRegion(JNIEnv* env, jobject, jlong brdHandle, jint in
recycledBitmap->setGainmap(std::move(gainmap));
}
bitmap::reinitBitmap(env, javaBitmap, recycledBitmap->info(), !requireUnpremul);
+ uirenderer::logBitmapDecode(*recycledBitmap);
return javaBitmap;
}
@@ -392,12 +394,14 @@ static jobject nativeDecodeRegion(JNIEnv* env, jobject, jlong brdHandle, jint in
hardwareBitmap->setGainmap(std::move(gm));
}
}
+ uirenderer::logBitmapDecode(*hardwareBitmap);
return bitmap::createBitmap(env, hardwareBitmap.release(), bitmapCreateFlags);
}
Bitmap* heapBitmap = heapAlloc.getStorageObjAndReset();
if (hasGainmap && heapBitmap != nullptr) {
heapBitmap->setGainmap(std::move(gainmap));
}
+ uirenderer::logBitmapDecode(*heapBitmap);
return android::bitmap::createBitmap(env, heapBitmap, bitmapCreateFlags);
}
diff --git a/libs/hwui/jni/ColorFilter.cpp b/libs/hwui/jni/ColorFilter.cpp
index 0b95148d3c82..20301d2c76ec 100644
--- a/libs/hwui/jni/ColorFilter.cpp
+++ b/libs/hwui/jni/ColorFilter.cpp
@@ -18,7 +18,9 @@
#include "ColorFilter.h"
#include "GraphicsJNI.h"
+#include "RuntimeEffectUtils.h"
#include "SkBlendMode.h"
+#include "include/effects/SkRuntimeEffect.h"
namespace android {
@@ -89,6 +91,78 @@ public:
filter->setMatrix(getMatrixFromJFloatArray(env, jarray));
}
}
+
+ static jlong RuntimeColorFilter_createColorFilter(JNIEnv* env, jobject, jstring agsl) {
+ ScopedUtfChars strSksl(env, agsl);
+ auto result = SkRuntimeEffect::MakeForColorFilter(SkString(strSksl.c_str()),
+ SkRuntimeEffect::Options{});
+ if (result.effect.get() == nullptr) {
+ doThrowIAE(env, result.errorText.c_str());
+ return 0;
+ }
+ auto builder = new SkRuntimeEffectBuilder(std::move(result.effect));
+ auto* runtimeColorFilter = new RuntimeColorFilter(builder);
+ runtimeColorFilter->incStrong(nullptr);
+ return static_cast<jlong>(reinterpret_cast<uintptr_t>(runtimeColorFilter));
+ }
+
+ static void RuntimeColorFilter_updateUniformsFloatArray(JNIEnv* env, jobject,
+ jlong colorFilterPtr,
+ jstring uniformName,
+ jfloatArray uniforms,
+ jboolean isColor) {
+ auto* filter = reinterpret_cast<RuntimeColorFilter*>(colorFilterPtr);
+ ScopedUtfChars name(env, uniformName);
+ AutoJavaFloatArray autoValues(env, uniforms, 0, kRO_JNIAccess);
+ if (filter) {
+ filter->updateUniforms(env, name.c_str(), autoValues.ptr(), autoValues.length(),
+ isColor);
+ }
+ }
+
+ static void RuntimeColorFilter_updateUniformsFloats(JNIEnv* env, jobject, jlong colorFilterPtr,
+ jstring uniformName, jfloat value1,
+ jfloat value2, jfloat value3, jfloat value4,
+ jint count) {
+ auto* filter = reinterpret_cast<RuntimeColorFilter*>(colorFilterPtr);
+ ScopedUtfChars name(env, uniformName);
+ const float values[4] = {value1, value2, value3, value4};
+ if (filter) {
+ filter->updateUniforms(env, name.c_str(), values, count, false);
+ }
+ }
+
+ static void RuntimeColorFilter_updateUniformsIntArray(JNIEnv* env, jobject,
+ jlong colorFilterPtr, jstring uniformName,
+ jintArray uniforms) {
+ auto* filter = reinterpret_cast<RuntimeColorFilter*>(colorFilterPtr);
+ ScopedUtfChars name(env, uniformName);
+ AutoJavaIntArray autoValues(env, uniforms, 0);
+ if (filter) {
+ filter->updateUniforms(env, name.c_str(), autoValues.ptr(), autoValues.length());
+ }
+ }
+
+ static void RuntimeColorFilter_updateUniformsInts(JNIEnv* env, jobject, jlong colorFilterPtr,
+ jstring uniformName, jint value1, jint value2,
+ jint value3, jint value4, jint count) {
+ auto* filter = reinterpret_cast<RuntimeColorFilter*>(colorFilterPtr);
+ ScopedUtfChars name(env, uniformName);
+ const int values[4] = {value1, value2, value3, value4};
+ if (filter) {
+ filter->updateUniforms(env, name.c_str(), values, count);
+ }
+ }
+
+ static void RuntimeColorFilter_updateChild(JNIEnv* env, jobject, jlong colorFilterPtr,
+ jstring childName, jlong childPtr) {
+ auto* filter = reinterpret_cast<RuntimeColorFilter*>(colorFilterPtr);
+ ScopedUtfChars name(env, childName);
+ auto* child = reinterpret_cast<SkFlattenable*>(childPtr);
+ if (filter && child) {
+ filter->updateChild(env, name.c_str(), child);
+ }
+ }
};
static const JNINativeMethod colorfilter_methods[] = {
@@ -107,6 +181,20 @@ static const JNINativeMethod colormatrix_methods[] = {
{"nativeColorMatrixFilter", "([F)J", (void*)ColorFilterGlue::CreateColorMatrixFilter},
{"nativeSetColorMatrix", "(J[F)V", (void*)ColorFilterGlue::SetColorMatrix}};
+static const JNINativeMethod runtime_color_filter_methods[] = {
+ {"nativeCreateRuntimeColorFilter", "(Ljava/lang/String;)J",
+ (void*)ColorFilterGlue::RuntimeColorFilter_createColorFilter},
+ {"nativeUpdateUniforms", "(JLjava/lang/String;[FZ)V",
+ (void*)ColorFilterGlue::RuntimeColorFilter_updateUniformsFloatArray},
+ {"nativeUpdateUniforms", "(JLjava/lang/String;FFFFI)V",
+ (void*)ColorFilterGlue::RuntimeColorFilter_updateUniformsFloats},
+ {"nativeUpdateUniforms", "(JLjava/lang/String;[I)V",
+ (void*)ColorFilterGlue::RuntimeColorFilter_updateUniformsIntArray},
+ {"nativeUpdateUniforms", "(JLjava/lang/String;IIIII)V",
+ (void*)ColorFilterGlue::RuntimeColorFilter_updateUniformsInts},
+ {"nativeUpdateChild", "(JLjava/lang/String;J)V",
+ (void*)ColorFilterGlue::RuntimeColorFilter_updateChild}};
+
int register_android_graphics_ColorFilter(JNIEnv* env) {
android::RegisterMethodsOrDie(env, "android/graphics/ColorFilter", colorfilter_methods,
NELEM(colorfilter_methods));
@@ -118,7 +206,10 @@ int register_android_graphics_ColorFilter(JNIEnv* env) {
NELEM(lighting_methods));
android::RegisterMethodsOrDie(env, "android/graphics/ColorMatrixColorFilter",
colormatrix_methods, NELEM(colormatrix_methods));
-
+ android::RegisterMethodsOrDie(env, "android/graphics/RuntimeColorFilter",
+ runtime_color_filter_methods,
+ NELEM(runtime_color_filter_methods));
+
return 0;
}
diff --git a/libs/hwui/jni/ImageDecoder.cpp b/libs/hwui/jni/ImageDecoder.cpp
index aebc4db37898..90fd3d87cba7 100644
--- a/libs/hwui/jni/ImageDecoder.cpp
+++ b/libs/hwui/jni/ImageDecoder.cpp
@@ -37,6 +37,7 @@
#include <hwui/Bitmap.h>
#include <hwui/ImageDecoder.h>
#include <sys/stat.h>
+#include <utils/StatsUtils.h>
#include "Bitmap.h"
#include "BitmapFactory.h"
@@ -485,6 +486,7 @@ static jobject ImageDecoder_nDecodeBitmap(JNIEnv* env, jobject /*clazz*/, jlong
hwBitmap->setGainmap(std::move(gm));
}
}
+ uirenderer::logBitmapDecode(*hwBitmap);
return bitmap::createBitmap(env, hwBitmap.release(), bitmapCreateFlags,
ninePatchChunk, ninePatchInsets);
}
@@ -498,6 +500,8 @@ static jobject ImageDecoder_nDecodeBitmap(JNIEnv* env, jobject /*clazz*/, jlong
nativeBitmap->setImmutable();
}
+
+ uirenderer::logBitmapDecode(*nativeBitmap);
return bitmap::createBitmap(env, nativeBitmap.release(), bitmapCreateFlags, ninePatchChunk,
ninePatchInsets);
}
diff --git a/libs/hwui/jni/RuntimeEffectUtils.cpp b/libs/hwui/jni/RuntimeEffectUtils.cpp
new file mode 100644
index 000000000000..46db8633c66e
--- /dev/null
+++ b/libs/hwui/jni/RuntimeEffectUtils.cpp
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "RuntimeEffectUtils.h"
+
+#include "include/effects/SkRuntimeEffect.h"
+
+namespace android {
+namespace uirenderer {
+
+static inline int ThrowIAEFmt(JNIEnv* env, const char* fmt, ...) {
+ va_list args;
+ va_start(args, fmt);
+ int ret = jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", fmt, args);
+ va_end(args);
+ return ret;
+}
+
+bool isIntUniformType(const SkRuntimeEffect::Uniform::Type& type) {
+ switch (type) {
+ case SkRuntimeEffect::Uniform::Type::kFloat:
+ case SkRuntimeEffect::Uniform::Type::kFloat2:
+ case SkRuntimeEffect::Uniform::Type::kFloat3:
+ case SkRuntimeEffect::Uniform::Type::kFloat4:
+ case SkRuntimeEffect::Uniform::Type::kFloat2x2:
+ case SkRuntimeEffect::Uniform::Type::kFloat3x3:
+ case SkRuntimeEffect::Uniform::Type::kFloat4x4:
+ return false;
+ case SkRuntimeEffect::Uniform::Type::kInt:
+ case SkRuntimeEffect::Uniform::Type::kInt2:
+ case SkRuntimeEffect::Uniform::Type::kInt3:
+ case SkRuntimeEffect::Uniform::Type::kInt4:
+ return true;
+ }
+}
+
+void UpdateFloatUniforms(JNIEnv* env, SkRuntimeEffectBuilder* builder, const char* uniformName,
+ const float values[], int count, bool isColor) {
+ SkRuntimeEffectBuilder::BuilderUniform uniform = builder->uniform(uniformName);
+ if (uniform.fVar == nullptr) {
+ ThrowIAEFmt(env, "unable to find uniform named %s", uniformName);
+ } else if (isColor != ((uniform.fVar->flags & SkRuntimeEffect::Uniform::kColor_Flag) != 0)) {
+ if (isColor) {
+ jniThrowExceptionFmt(
+ env, "java/lang/IllegalArgumentException",
+ "attempting to set a color uniform using the non-color specific APIs: %s %x",
+ uniformName, uniform.fVar->flags);
+ } else {
+ ThrowIAEFmt(env,
+ "attempting to set a non-color uniform using the setColorUniform APIs: %s",
+ uniformName);
+ }
+ } else if (isIntUniformType(uniform.fVar->type)) {
+ ThrowIAEFmt(env, "attempting to set a int uniform using the setUniform APIs: %s",
+ uniformName);
+ } else if (!uniform.set<float>(values, count)) {
+ ThrowIAEFmt(env, "mismatch in byte size for uniform [expected: %zu actual: %zu]",
+ uniform.fVar->sizeInBytes(), sizeof(float) * count);
+ }
+}
+
+void UpdateIntUniforms(JNIEnv* env, SkRuntimeEffectBuilder* builder, const char* uniformName,
+ const int values[], int count) {
+ SkRuntimeEffectBuilder::BuilderUniform uniform = builder->uniform(uniformName);
+ if (uniform.fVar == nullptr) {
+ ThrowIAEFmt(env, "unable to find uniform named %s", uniformName);
+ } else if (!isIntUniformType(uniform.fVar->type)) {
+ ThrowIAEFmt(env, "attempting to set a non-int uniform using the setIntUniform APIs: %s",
+ uniformName);
+ } else if (!uniform.set<int>(values, count)) {
+ ThrowIAEFmt(env, "mismatch in byte size for uniform [expected: %zu actual: %zu]",
+ uniform.fVar->sizeInBytes(), sizeof(float) * count);
+ }
+}
+
+void UpdateChild(JNIEnv* env, SkRuntimeEffectBuilder* builder, const char* childName,
+ SkFlattenable* childEffect) {
+ SkRuntimeShaderBuilder::BuilderChild builderChild = builder->child(childName);
+ if (builderChild.fChild == nullptr) {
+ ThrowIAEFmt(env, "unable to find shader named %s", childName);
+ return;
+ }
+
+ builderChild = sk_ref_sp(childEffect);
+}
+
+} // namespace uirenderer
+} // namespace android \ No newline at end of file
diff --git a/libs/hwui/jni/RuntimeEffectUtils.h b/libs/hwui/jni/RuntimeEffectUtils.h
new file mode 100644
index 000000000000..75623c0f7ac1
--- /dev/null
+++ b/libs/hwui/jni/RuntimeEffectUtils.h
@@ -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.
+ */
+
+#ifndef RUNTIMEEFFECTUTILS_H
+#define RUNTIMEEFFECTUTILS_H
+
+#include "GraphicsJNI.h"
+#include "include/effects/SkRuntimeEffect.h"
+
+namespace android {
+namespace uirenderer {
+
+void UpdateFloatUniforms(JNIEnv* env, SkRuntimeEffectBuilder* builder, const char* uniformName,
+ const float values[], int count, bool isColor);
+
+void UpdateIntUniforms(JNIEnv* env, SkRuntimeEffectBuilder* builder, const char* uniformName,
+ const int values[], int count);
+
+void UpdateChild(JNIEnv* env, SkRuntimeEffectBuilder* builder, const char* childName,
+ SkFlattenable* childEffect);
+} // namespace uirenderer
+} // namespace android
+
+#endif // MAIN_RUNTIMEEFFECTUTILS_H
diff --git a/libs/hwui/jni/android_graphics_RenderNode.cpp b/libs/hwui/jni/android_graphics_RenderNode.cpp
index 6e03bbd0fa16..d9dc8eb8c1b1 100644
--- a/libs/hwui/jni/android_graphics_RenderNode.cpp
+++ b/libs/hwui/jni/android_graphics_RenderNode.cpp
@@ -593,9 +593,9 @@ static void android_view_RenderNode_requestPositionUpdates(JNIEnv* env, jobject,
Matrix4 transform;
SkIRect clipBounds;
+ uirenderer::Rect initialClipBounds;
+ const auto clipFlags = props.getClippingFlags();
if (enableClip) {
- uirenderer::Rect initialClipBounds;
- const auto clipFlags = props.getClippingFlags();
if (clipFlags) {
props.getClippingRectForFlags(clipFlags, &initialClipBounds);
} else {
@@ -659,8 +659,8 @@ static void android_view_RenderNode_requestPositionUpdates(JNIEnv* env, jobject,
static_cast<jint>(bounds.left), static_cast<jint>(bounds.top),
static_cast<jint>(bounds.right), static_cast<jint>(bounds.bottom),
static_cast<jint>(clipBounds.fLeft), static_cast<jint>(clipBounds.fTop),
- static_cast<jint>(clipBounds.fRight),
- static_cast<jint>(clipBounds.fBottom));
+ static_cast<jint>(clipBounds.fRight), static_cast<jint>(clipBounds.fBottom),
+ static_cast<jint>(props.getWidth()), static_cast<jint>(props.getHeight()));
}
if (!keepListening) {
env->DeleteGlobalRef(mListener);
@@ -891,7 +891,7 @@ int register_android_view_RenderNode(JNIEnv* env) {
gPositionListener.callPositionChanged = GetStaticMethodIDOrDie(
env, clazz, "callPositionChanged", "(Ljava/lang/ref/WeakReference;JIIII)Z");
gPositionListener.callPositionChanged2 = GetStaticMethodIDOrDie(
- env, clazz, "callPositionChanged2", "(Ljava/lang/ref/WeakReference;JIIIIIIII)Z");
+ env, clazz, "callPositionChanged2", "(Ljava/lang/ref/WeakReference;JIIIIIIIIII)Z");
gPositionListener.callApplyStretch = GetStaticMethodIDOrDie(
env, clazz, "callApplyStretch", "(Ljava/lang/ref/WeakReference;JFFFFFFFFFF)Z");
gPositionListener.callPositionLost = GetStaticMethodIDOrDie(
diff --git a/libs/hwui/libhwui.map.txt b/libs/hwui/libhwui.map.txt
index 2414299321a9..b5591941453d 100644
--- a/libs/hwui/libhwui.map.txt
+++ b/libs/hwui/libhwui.map.txt
@@ -67,6 +67,7 @@ LIBHWUI_PLATFORM {
SkFILEStream::SkFILEStream*;
SkImageInfo::*;
SkMemoryStream::SkMemoryStream*;
+ android::uirenderer::logBitmapDecode*;
};
local:
*;
diff --git a/libs/hwui/utils/StatsUtils.cpp b/libs/hwui/utils/StatsUtils.cpp
new file mode 100644
index 000000000000..5c4027e1a846
--- /dev/null
+++ b/libs/hwui/utils/StatsUtils.cpp
@@ -0,0 +1,102 @@
+/*
+ * 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.
+ */
+
+#ifdef __ANDROID__
+#include <dlfcn.h>
+#include <log/log.h>
+#include <statslog_hwui.h>
+#include <statssocket_lazy.h>
+#include <utils/Errors.h>
+
+#include <mutex>
+#endif
+
+#include <unistd.h>
+
+#include "StatsUtils.h"
+
+namespace android {
+namespace uirenderer {
+
+#ifdef __ANDROID__
+
+namespace {
+
+int32_t toStatsColorSpaceTransfer(skcms_TFType transferType) {
+ switch (transferType) {
+ case skcms_TFType_sRGBish:
+ return stats::IMAGE_DECODED__COLOR_SPACE_TRANSFER__COLOR_SPACE_TRANSFER_SRGBISH;
+ case skcms_TFType_PQish:
+ return stats::IMAGE_DECODED__COLOR_SPACE_TRANSFER__COLOR_SPACE_TRANSFER_PQISH;
+ case skcms_TFType_HLGish:
+ return stats::IMAGE_DECODED__COLOR_SPACE_TRANSFER__COLOR_SPACE_TRANSFER_HLGISH;
+ default:
+ return stats::IMAGE_DECODED__COLOR_SPACE_TRANSFER__COLOR_SPACE_TRANSFER_UNKNOWN;
+ }
+}
+
+int32_t toStatsBitmapFormat(SkColorType type) {
+ switch (type) {
+ case kAlpha_8_SkColorType:
+ return stats::IMAGE_DECODED__FORMAT__BITMAP_FORMAT_A_8;
+ case kRGB_565_SkColorType:
+ return stats::IMAGE_DECODED__FORMAT__BITMAP_FORMAT_RGB_565;
+ case kN32_SkColorType:
+ return stats::IMAGE_DECODED__FORMAT__BITMAP_FORMAT_ARGB_8888;
+ case kRGBA_F16_SkColorType:
+ return stats::IMAGE_DECODED__FORMAT__BITMAP_FORMAT_RGBA_F16;
+ case kRGBA_1010102_SkColorType:
+ return stats::IMAGE_DECODED__FORMAT__BITMAP_FORMAT_RGBA_1010102;
+ default:
+ return stats::IMAGE_DECODED__FORMAT__BITMAP_FORMAT_UNKNOWN;
+ }
+}
+
+} // namespace
+
+#endif
+
+void logBitmapDecode(const SkImageInfo& info, bool hasGainmap) {
+#ifdef __ANDROID__
+
+ if (!statssocket::lazy::IsAvailable()) {
+ std::once_flag once;
+ std::call_once(once, []() { ALOGD("libstatssocket not available, dropping stats"); });
+ return;
+ }
+
+ skcms_TFType tfnType = skcms_TFType_Invalid;
+
+ if (info.colorSpace()) {
+ skcms_TransferFunction tfn;
+ info.colorSpace()->transferFn(&tfn);
+ tfnType = skcms_TransferFunction_getType(&tfn);
+ }
+
+ auto status =
+ stats::stats_write(uirenderer::stats::IMAGE_DECODED, static_cast<int32_t>(getuid()),
+ uirenderer::toStatsColorSpaceTransfer(tfnType), hasGainmap,
+ uirenderer::toStatsBitmapFormat(info.colorType()));
+ ALOGW_IF(status != OK, "Image decoding logging dropped!");
+#endif
+}
+
+void logBitmapDecode(const Bitmap& bitmap) {
+ logBitmapDecode(bitmap.info(), bitmap.hasGainmap());
+}
+
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/utils/StatsUtils.h b/libs/hwui/utils/StatsUtils.h
new file mode 100644
index 000000000000..0c247014a8eb
--- /dev/null
+++ b/libs/hwui/utils/StatsUtils.h
@@ -0,0 +1,33 @@
+/*
+ * 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 <SkBitmap.h>
+#include <SkColorSpace.h>
+#include <SkColorType.h>
+#include <cutils/compiler.h>
+#include <hwui/Bitmap.h>
+
+namespace android {
+namespace uirenderer {
+
+ANDROID_API void logBitmapDecode(const SkImageInfo& info, bool hasGainmap);
+
+ANDROID_API void logBitmapDecode(const Bitmap& bitmap);
+
+} // namespace uirenderer
+} // namespace android
diff --git a/media/java/android/media/AudioAttributes.java b/media/java/android/media/AudioAttributes.java
index 5a8eb3ab17b8..1db719828c76 100644
--- a/media/java/android/media/AudioAttributes.java
+++ b/media/java/android/media/AudioAttributes.java
@@ -16,15 +16,11 @@
package android.media;
-import static android.media.audio.Flags.FLAG_SPEAKER_CLEANUP_USAGE;
-
-import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.annotation.TestApi;
-// TODO switch from HIDL imports to AIDL
import android.audio.policy.configuration.V7_0.AudioUsage;
import android.compat.annotation.UnsupportedAppUsage;
import android.media.audiopolicy.AudioProductStrategy;
@@ -251,16 +247,6 @@ public final class AudioAttributes implements Parcelable {
public static final int USAGE_ANNOUNCEMENT = SYSTEM_USAGE_OFFSET + 3;
/**
- * @hide
- * Usage value to use when a system application plays a signal intended to clean up the
- * speaker transducers and free them of deposits of dust or water.
- */
- @FlaggedApi(FLAG_SPEAKER_CLEANUP_USAGE)
- @SystemApi
- @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
- public static final int USAGE_SPEAKER_CLEANUP = SYSTEM_USAGE_OFFSET + 4;
-
- /**
* IMPORTANT: when adding new usage types, add them to SDK_USAGES and update SUPPRESSIBLE_USAGES
* if applicable, as well as audioattributes.proto.
* Also consider adding them to <aaudio/AAudio.h> for the NDK.
@@ -1216,7 +1202,6 @@ public final class AudioAttributes implements Parcelable {
break;
case AudioSystem.STREAM_BLUETOOTH_SCO:
mContentType = CONTENT_TYPE_SPEECH;
- mFlags |= FLAG_SCO;
break;
case AudioSystem.STREAM_DTMF:
mContentType = CONTENT_TYPE_SONIFICATION;
@@ -1536,8 +1521,6 @@ public final class AudioAttributes implements Parcelable {
return "USAGE_VEHICLE_STATUS";
case USAGE_ANNOUNCEMENT:
return "USAGE_ANNOUNCEMENT";
- case USAGE_SPEAKER_CLEANUP:
- return "USAGE_SPEAKER_CLEANUP";
default:
return "unknown usage " + usage;
}
@@ -1678,8 +1661,12 @@ public final class AudioAttributes implements Parcelable {
}
/**
- * Returns whether the given usage can only be used by system-privileged components
- * @param usage one of {@link AttributeSystemUsage}.
+ * @param usage one of {@link AttributeSystemUsage},
+ * {@link AttributeSystemUsage#USAGE_CALL_ASSISTANT},
+ * {@link AttributeSystemUsage#USAGE_EMERGENCY},
+ * {@link AttributeSystemUsage#USAGE_SAFETY},
+ * {@link AttributeSystemUsage#USAGE_VEHICLE_STATUS},
+ * {@link AttributeSystemUsage#USAGE_ANNOUNCEMENT}
* @return boolean indicating if the usage is a system usage or not
* @hide
*/
@@ -1689,8 +1676,7 @@ public final class AudioAttributes implements Parcelable {
|| usage == USAGE_EMERGENCY
|| usage == USAGE_SAFETY
|| usage == USAGE_VEHICLE_STATUS
- || usage == USAGE_ANNOUNCEMENT
- || usage == USAGE_SPEAKER_CLEANUP);
+ || usage == USAGE_ANNOUNCEMENT);
}
/**
@@ -1763,8 +1749,7 @@ public final class AudioAttributes implements Parcelable {
AudioSystem.STREAM_SYSTEM : AudioSystem.STREAM_SYSTEM_ENFORCED;
}
if ((aa.getAllFlags() & FLAG_SCO) == FLAG_SCO) {
- return fromGetVolumeControlStream ?
- AudioSystem.STREAM_VOICE_CALL : AudioSystem.STREAM_BLUETOOTH_SCO;
+ return AudioSystem.STREAM_VOICE_CALL;
}
if ((aa.getAllFlags() & FLAG_BEACON) == FLAG_BEACON) {
return fromGetVolumeControlStream ?
@@ -1805,7 +1790,6 @@ public final class AudioAttributes implements Parcelable {
case USAGE_SAFETY:
case USAGE_VEHICLE_STATUS:
case USAGE_ANNOUNCEMENT:
- case USAGE_SPEAKER_CLEANUP:
case USAGE_UNKNOWN:
return AudioSystem.STREAM_MUSIC;
default:
@@ -1845,8 +1829,7 @@ public final class AudioAttributes implements Parcelable {
USAGE_EMERGENCY,
USAGE_SAFETY,
USAGE_VEHICLE_STATUS,
- USAGE_ANNOUNCEMENT,
- USAGE_SPEAKER_CLEANUP
+ USAGE_ANNOUNCEMENT
})
@Retention(RetentionPolicy.SOURCE)
public @interface AttributeSystemUsage {}
@@ -1896,7 +1879,6 @@ public final class AudioAttributes implements Parcelable {
USAGE_SAFETY,
USAGE_VEHICLE_STATUS,
USAGE_ANNOUNCEMENT,
- USAGE_SPEAKER_CLEANUP,
})
@Retention(RetentionPolicy.SOURCE)
public @interface AttributeUsage {}
diff --git a/media/java/android/media/AudioDeviceInfo.java b/media/java/android/media/AudioDeviceInfo.java
index 39b29d0156d2..2da8eecedc4d 100644
--- a/media/java/android/media/AudioDeviceInfo.java
+++ b/media/java/android/media/AudioDeviceInfo.java
@@ -16,11 +16,15 @@
package android.media;
+import static android.media.audio.Flags.FLAG_ENABLE_MULTICHANNEL_GROUP_DEVICE;
+
import android.Manifest;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
import android.annotation.TestApi;
+import android.media.audio.Flags;
import android.util.SparseIntArray;
import com.android.internal.annotations.VisibleForTesting;
@@ -192,6 +196,15 @@ public final class AudioDeviceInfo {
*/
public static final int TYPE_DOCK_ANALOG = 31;
+ /**
+ * A device type describing a speaker group that supports multichannel contents. The speakers in
+ * the group are connected together using local network based protocols. The speaker group
+ * requires additional input of the physical positions of each individual speaker to provide a
+ * better experience on multichannel contents.
+ */
+ @FlaggedApi(FLAG_ENABLE_MULTICHANNEL_GROUP_DEVICE)
+ public static final int TYPE_MULTICHANNEL_GROUP = 32;
+
/** @hide */
@IntDef(flag = false, prefix = "TYPE", value = {
TYPE_BUILTIN_EARPIECE,
@@ -224,7 +237,8 @@ public final class AudioDeviceInfo {
TYPE_BLE_SPEAKER,
TYPE_ECHO_REFERENCE,
TYPE_BLE_BROADCAST,
- TYPE_DOCK_ANALOG}
+ TYPE_DOCK_ANALOG,
+ TYPE_MULTICHANNEL_GROUP}
)
@Retention(RetentionPolicy.SOURCE)
public @interface AudioDeviceType {}
@@ -285,7 +299,8 @@ public final class AudioDeviceInfo {
TYPE_BLE_HEADSET,
TYPE_BLE_SPEAKER,
TYPE_BLE_BROADCAST,
- TYPE_DOCK_ANALOG}
+ TYPE_DOCK_ANALOG,
+ TYPE_MULTICHANNEL_GROUP}
)
@Retention(RetentionPolicy.SOURCE)
public @interface AudioDeviceTypeOut {}
@@ -321,7 +336,13 @@ public final class AudioDeviceInfo {
case TYPE_BLE_BROADCAST:
case TYPE_DOCK_ANALOG:
return true;
+
default:
+ if (Flags.enableMultichannelGroupDevice()) {
+ if (type == TYPE_MULTICHANNEL_GROUP) {
+ return true;
+ }
+ }
return false;
}
}
@@ -665,6 +686,10 @@ public final class AudioDeviceInfo {
INT_TO_EXT_DEVICE_MAPPING.put(AudioSystem.DEVICE_OUT_BLE_HEADSET, TYPE_BLE_HEADSET);
INT_TO_EXT_DEVICE_MAPPING.put(AudioSystem.DEVICE_OUT_BLE_SPEAKER, TYPE_BLE_SPEAKER);
INT_TO_EXT_DEVICE_MAPPING.put(AudioSystem.DEVICE_OUT_BLE_BROADCAST, TYPE_BLE_BROADCAST);
+ if (Flags.enableMultichannelGroupDevice()) {
+ INT_TO_EXT_DEVICE_MAPPING.put(
+ AudioSystem.DEVICE_OUT_MULTICHANNEL_GROUP, TYPE_MULTICHANNEL_GROUP);
+ }
INT_TO_EXT_DEVICE_MAPPING.put(AudioSystem.DEVICE_IN_BUILTIN_MIC, TYPE_BUILTIN_MIC);
INT_TO_EXT_DEVICE_MAPPING.put(AudioSystem.DEVICE_IN_BLUETOOTH_SCO_HEADSET, TYPE_BLUETOOTH_SCO);
@@ -721,6 +746,10 @@ public final class AudioDeviceInfo {
EXT_TO_INT_DEVICE_MAPPING.put(TYPE_BLE_HEADSET, AudioSystem.DEVICE_OUT_BLE_HEADSET);
EXT_TO_INT_DEVICE_MAPPING.put(TYPE_BLE_SPEAKER, AudioSystem.DEVICE_OUT_BLE_SPEAKER);
EXT_TO_INT_DEVICE_MAPPING.put(TYPE_BLE_BROADCAST, AudioSystem.DEVICE_OUT_BLE_BROADCAST);
+ if (Flags.enableMultichannelGroupDevice()) {
+ EXT_TO_INT_DEVICE_MAPPING.put(
+ TYPE_MULTICHANNEL_GROUP, AudioSystem.DEVICE_OUT_MULTICHANNEL_GROUP);
+ }
// privileges mapping to input device
EXT_TO_INT_INPUT_DEVICE_MAPPING = new SparseIntArray();
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 8250a536552e..dfe291605522 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -6156,6 +6156,11 @@ public class AudioManager {
*/
public static final int DEVICE_OUT_BLE_BROADCAST = AudioSystem.DEVICE_OUT_BLE_BROADCAST;
/** @hide
+ * The audio output device code for a wireless speaker group supporting multichannel content.
+ */
+ public static final int DEVICE_OUT_MULTICHANNEL_GROUP =
+ AudioSystem.DEVICE_OUT_MULTICHANNEL_GROUP;
+ /** @hide
* This is not used as a returned value from {@link #getDevicesForStream}, but could be
* used in the future in a set method to select whatever default device is chosen by the
* platform-specific implementation.
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index bf09cb07a8ed..0ab94f567506 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -1066,6 +1066,8 @@ public class AudioSystem
/** @hide */
public static final int DEVICE_OUT_IP = 0x800000;
/** @hide */
+ public static final int DEVICE_OUT_MULTICHANNEL_GROUP = 0x800001;
+ /** @hide */
public static final int DEVICE_OUT_BUS = 0x1000000;
/** @hide */
public static final int DEVICE_OUT_PROXY = 0x2000000;
@@ -1134,6 +1136,7 @@ public class AudioSystem
DEVICE_OUT_ALL_SET.add(DEVICE_OUT_AUX_LINE);
DEVICE_OUT_ALL_SET.add(DEVICE_OUT_SPEAKER_SAFE);
DEVICE_OUT_ALL_SET.add(DEVICE_OUT_IP);
+ DEVICE_OUT_ALL_SET.add(DEVICE_OUT_MULTICHANNEL_GROUP);
DEVICE_OUT_ALL_SET.add(DEVICE_OUT_BUS);
DEVICE_OUT_ALL_SET.add(DEVICE_OUT_PROXY);
DEVICE_OUT_ALL_SET.add(DEVICE_OUT_USB_HEADSET);
@@ -1422,6 +1425,8 @@ public class AudioSystem
/** @hide */ public static final String DEVICE_OUT_AUX_LINE_NAME = "aux_line";
/** @hide */ public static final String DEVICE_OUT_SPEAKER_SAFE_NAME = "speaker_safe";
/** @hide */ public static final String DEVICE_OUT_IP_NAME = "ip";
+ /** @hide */
+ public static final String DEVICE_OUT_MULTICHANNEL_GROUP_NAME = "multichannel_group";
/** @hide */ public static final String DEVICE_OUT_BUS_NAME = "bus";
/** @hide */ public static final String DEVICE_OUT_PROXY_NAME = "proxy";
/** @hide */ public static final String DEVICE_OUT_USB_HEADSET_NAME = "usb_headset";
@@ -1515,6 +1520,8 @@ public class AudioSystem
return DEVICE_OUT_SPEAKER_SAFE_NAME;
case DEVICE_OUT_IP:
return DEVICE_OUT_IP_NAME;
+ case DEVICE_OUT_MULTICHANNEL_GROUP:
+ return DEVICE_OUT_MULTICHANNEL_GROUP_NAME;
case DEVICE_OUT_BUS:
return DEVICE_OUT_BUS_NAME;
case DEVICE_OUT_PROXY:
diff --git a/media/java/android/media/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java
index 3a19f466f7c1..96edd63a9b12 100644
--- a/media/java/android/media/MediaCodecInfo.java
+++ b/media/java/android/media/MediaCodecInfo.java
@@ -23,6 +23,7 @@ import static android.media.codec.Flags.FLAG_HLG_EDITING;
import static android.media.codec.Flags.FLAG_IN_PROCESS_SW_AUDIO_CODEC;
import static android.media.codec.Flags.FLAG_NULL_OUTPUT_SURFACE;
import static android.media.codec.Flags.FLAG_REGION_OF_INTEREST;
+import static android.media.codec.Flags.FLAG_APV_SUPPORT;
import static android.media.MediaCodec.GetFlag;
import android.annotation.FlaggedApi;
@@ -4496,6 +4497,265 @@ public final class MediaCodecInfo {
@SuppressLint("AllUpper")
public static final int AC4Level4 = 0x10;
+ // Profiles and levels/bands for APV Codec, corresponding to the definitions in
+ // "Advanced Professional Video", 10.1.3 Profiles, 10.1.4 Levels and Bands
+ // found at https://www.ietf.org/archive/id/draft-lim-apv-02.html
+
+ /**
+ * APV codec profile 422-10 as per IETF lim-apv-02, 10.1.3.1.1
+ */
+ @SuppressLint("AllUpper")
+ @FlaggedApi(FLAG_APV_SUPPORT)
+ public static final int APVProfile422_10 = 0x01;
+
+ /**
+ * APV codec profile 422-10 as per IETF lim-apv-02, 10.1.3.1.1
+ * with HDR10.
+ */
+ @SuppressLint("AllUpper")
+ @FlaggedApi(FLAG_APV_SUPPORT)
+ public static final int APVProfile422_10HDR10 = 0x1000;
+
+ /**
+ * APV codec profile 422-10 as per IETF lim-apv-02, 10.1.3.1.1
+ * with HDR10Plus.
+ */
+ @SuppressLint("AllUpper")
+ @FlaggedApi(FLAG_APV_SUPPORT)
+ public static final int APVProfile422_10HDR10Plus = 0x2000;
+
+ // For APV Levels, the numerical values are constructed as follows:
+ // ((0x100 << (level_num - 1)) | (1 << band))
+ // where:
+ // - "level_num" is the APV Level numbered consecutively
+ // (i.e., Level 1 == 1, Level 1.1 == 2, etc.)
+ // - "band" is the APV Band
+
+ /** APV Codec Level 1, Band 0 as per IETF lim-apv-02, 10.1.4 */
+ @SuppressLint("AllUpper")
+ @FlaggedApi(FLAG_APV_SUPPORT)
+ public static final int APVLevel1Band0 = 0x101;
+ /** APV Codec Level 1, Band 1 as per IETF lim-apv-02, 10.1.4 */
+ @SuppressLint("AllUpper")
+ @FlaggedApi(FLAG_APV_SUPPORT)
+ public static final int APVLevel1Band1 = 0x102;
+ /** APV Codec Level 1, Band 2 as per IETF lim-apv-02, 10.1.4 */
+ @SuppressLint("AllUpper")
+ @FlaggedApi(FLAG_APV_SUPPORT)
+ public static final int APVLevel1Band2 = 0x104;
+ /** APV Codec Level 1, Band 3 as per IETF lim-apv-02, 10.1.4 */
+ @SuppressLint("AllUpper")
+ @FlaggedApi(FLAG_APV_SUPPORT)
+ public static final int APVLevel1Band3 = 0x108;
+ /** APV Codec Level 1.1, Band 0 as per IETF lim-apv-02, 10.1.4 */
+ @SuppressLint("AllUpper")
+ @FlaggedApi(FLAG_APV_SUPPORT)
+ public static final int APVLevel11Band0 = 0x201;
+ /** APV Codec Level 1.1, Band 1 as per IETF lim-apv-02, 10.1.4 */
+ @SuppressLint("AllUpper")
+ @FlaggedApi(FLAG_APV_SUPPORT)
+ public static final int APVLevel11Band1 = 0x202;
+ /** APV Codec Level 1.1, Band 2 as per IETF lim-apv-02, 10.1.4 */
+ @SuppressLint("AllUpper")
+ @FlaggedApi(FLAG_APV_SUPPORT)
+ public static final int APVLevel11Band2 = 0x204;
+ /** APV Codec Level 1.1, Band 3 as per IETF lim-apv-02, 10.1.4 */
+ @SuppressLint("AllUpper")
+ @FlaggedApi(FLAG_APV_SUPPORT)
+ public static final int APVLevel11Band3 = 0x208;
+ /** APV Codec Level 2, Band 0 as per IETF lim-apv-02, 10.1.4 */
+ @SuppressLint("AllUpper")
+ @FlaggedApi(FLAG_APV_SUPPORT)
+ public static final int APVLevel2Band0 = 0x401;
+ /** APV Codec Level 2, Band 1 as per IETF lim-apv-02, 10.1.4 */
+ @SuppressLint("AllUpper")
+ @FlaggedApi(FLAG_APV_SUPPORT)
+ public static final int APVLevel2Band1 = 0x402;
+ /** APV Codec Level 2, Band 2 as per IETF lim-apv-02, 10.1.4 */
+ @SuppressLint("AllUpper")
+ @FlaggedApi(FLAG_APV_SUPPORT)
+ public static final int APVLevel2Band2 = 0x404;
+ /** APV Codec Level 2, Band 3 as per IETF lim-apv-02, 10.1.4 */
+ @SuppressLint("AllUpper")
+ @FlaggedApi(FLAG_APV_SUPPORT)
+ public static final int APVLevel2Band3 = 0x408;
+ /** APV Codec Level 2.1, Band 0 as per IETF lim-apv-02, 10.1.4 */
+ @SuppressLint("AllUpper")
+ @FlaggedApi(FLAG_APV_SUPPORT)
+ public static final int APVLevel21Band0 = 0x801;
+ /** APV Codec Level 2.1, Band 1 as per IETF lim-apv-02, 10.1.4 */
+ @SuppressLint("AllUpper")
+ @FlaggedApi(FLAG_APV_SUPPORT)
+ public static final int APVLevel21Band1 = 0x802;
+ /** APV Codec Level 2.1, Band 2 as per IETF lim-apv-02, 10.1.4 */
+ @SuppressLint("AllUpper")
+ @FlaggedApi(FLAG_APV_SUPPORT)
+ public static final int APVLevel21Band2 = 0x804;
+ /** APV Codec Level 2.1, Band 3 as per IETF lim-apv-02, 10.1.4 */
+ @SuppressLint("AllUpper")
+ @FlaggedApi(FLAG_APV_SUPPORT)
+ public static final int APVLevel21Band3 = 0x808;
+ /** APV Codec Level 3, Band 0 as per IETF lim-apv-02, 10.1.4 */
+ @SuppressLint("AllUpper")
+ @FlaggedApi(FLAG_APV_SUPPORT)
+ public static final int APVLevel3Band0 = 0x1001;
+ /** APV Codec Level 3, Band 1 as per IETF lim-apv-02, 10.1.4 */
+ @SuppressLint("AllUpper")
+ @FlaggedApi(FLAG_APV_SUPPORT)
+ public static final int APVLevel3Band1 = 0x1002;
+ /** APV Codec Level 3, Band 2 as per IETF lim-apv-02, 10.1.4 */
+ @SuppressLint("AllUpper")
+ @FlaggedApi(FLAG_APV_SUPPORT)
+ public static final int APVLevel3Band2 = 0x1004;
+ /** APV Codec Level 3, Band 3 as per IETF lim-apv-02, 10.1.4 */
+ @SuppressLint("AllUpper")
+ @FlaggedApi(FLAG_APV_SUPPORT)
+ public static final int APVLevel3Band3 = 0x1008;
+ /** APV Codec Level 3.1, Band 0 as per IETF lim-apv-02, 10.1.4 */
+ @SuppressLint("AllUpper")
+ @FlaggedApi(FLAG_APV_SUPPORT)
+ public static final int APVLevel31Band0 = 0x2001;
+ /** APV Codec Level 3.1, Band 1 as per IETF lim-apv-02, 10.1.4 */
+ @SuppressLint("AllUpper")
+ @FlaggedApi(FLAG_APV_SUPPORT)
+ public static final int APVLevel31Band1 = 0x2002;
+ /** APV Codec Level 3.1, Band 2 as per IETF lim-apv-02, 10.1.4 */
+ @SuppressLint("AllUpper")
+ @FlaggedApi(FLAG_APV_SUPPORT)
+ public static final int APVLevel31Band2 = 0x2004;
+ /** APV Codec Level 3.1, Band 3 as per IETF lim-apv-02, 10.1.4 */
+ @SuppressLint("AllUpper")
+ @FlaggedApi(FLAG_APV_SUPPORT)
+ public static final int APVLevel31Band3 = 0x2008;
+ /** APV Codec Level 4, Band 0 as per IETF lim-apv-02, 10.1.4 */
+ @SuppressLint("AllUpper")
+ @FlaggedApi(FLAG_APV_SUPPORT)
+ public static final int APVLevel4Band0 = 0x4001;
+ /** APV Codec Level 4, Band 1 as per IETF lim-apv-02, 10.1.4 */
+ @SuppressLint("AllUpper")
+ @FlaggedApi(FLAG_APV_SUPPORT)
+ public static final int APVLevel4Band1 = 0x4002;
+ /** APV Codec Level 4, Band 2 as per IETF lim-apv-02, 10.1.4 */
+ @SuppressLint("AllUpper")
+ @FlaggedApi(FLAG_APV_SUPPORT)
+ public static final int APVLevel4Band2 = 0x4004;
+ /** APV Codec Level 4, Band 3 as per IETF lim-apv-02, 10.1.4 */
+ @SuppressLint("AllUpper")
+ @FlaggedApi(FLAG_APV_SUPPORT)
+ public static final int APVLevel4Band3 = 0x4008;
+ /** APV Codec Level 4.1, Band 0 as per IETF lim-apv-02, 10.1.4 */
+ @SuppressLint("AllUpper")
+ @FlaggedApi(FLAG_APV_SUPPORT)
+ public static final int APVLevel41Band0 = 0x8001;
+ /** APV Codec Level 4.1, Band 1 as per IETF lim-apv-02, 10.1.4 */
+ @SuppressLint("AllUpper")
+ @FlaggedApi(FLAG_APV_SUPPORT)
+ public static final int APVLevel41Band1 = 0x8002;
+ /** APV Codec Level 4.1, Band 2 as per IETF lim-apv-02, 10.1.4 */
+ @SuppressLint("AllUpper")
+ @FlaggedApi(FLAG_APV_SUPPORT)
+ public static final int APVLevel41Band2 = 0x8004;
+ /** APV Codec Level 4.1, Band 3 as per IETF lim-apv-02, 10.1.4 */
+ @SuppressLint("AllUpper")
+ @FlaggedApi(FLAG_APV_SUPPORT)
+ public static final int APVLevel41Band3 = 0x8008;
+ /** APV Codec Level 5, Band 0 as per IETF lim-apv-02, 10.1.4 */
+ @SuppressLint("AllUpper")
+ @FlaggedApi(FLAG_APV_SUPPORT)
+ public static final int APVLevel5Band0 = 0x10001;
+ /** APV Codec Level 5, Band 1 as per IETF lim-apv-02, 10.1.4 */
+ @SuppressLint("AllUpper")
+ @FlaggedApi(FLAG_APV_SUPPORT)
+ public static final int APVLevel5Band1 = 0x10002;
+ /** APV Codec Level 5, Band 2 as per IETF lim-apv-02, 10.1.4 */
+ @SuppressLint("AllUpper")
+ @FlaggedApi(FLAG_APV_SUPPORT)
+ public static final int APVLevel5Band2 = 0x10004;
+ /** APV Codec Level 5, Band 3 as per IETF lim-apv-02, 10.1.4 */
+ @SuppressLint("AllUpper")
+ @FlaggedApi(FLAG_APV_SUPPORT)
+ public static final int APVLevel5Band3 = 0x10008;
+ /** APV Codec Level 5.1, Band 0 as per IETF lim-apv-02, 10.1.4 */
+ @SuppressLint("AllUpper")
+ @FlaggedApi(FLAG_APV_SUPPORT)
+ public static final int APVLevel51Band0 = 0x20001;
+ /** APV Codec Level 5.1, Band 1 as per IETF lim-apv-02, 10.1.4 */
+ @SuppressLint("AllUpper")
+ @FlaggedApi(FLAG_APV_SUPPORT)
+ public static final int APVLevel51Band1 = 0x20002;
+ /** APV Codec Level 5.1, Band 2 as per IETF lim-apv-02, 10.1.4 */
+ @SuppressLint("AllUpper")
+ @FlaggedApi(FLAG_APV_SUPPORT)
+ public static final int APVLevel51Band2 = 0x20004;
+ /** APV Codec Level 5.1, Band 3 as per IETF lim-apv-02, 10.1.4 */
+ @SuppressLint("AllUpper")
+ @FlaggedApi(FLAG_APV_SUPPORT)
+ public static final int APVLevel51Band3 = 0x20008;
+ /** APV Codec Level 6, Band 0 as per IETF lim-apv-02, 10.1.4 */
+ @SuppressLint("AllUpper")
+ @FlaggedApi(FLAG_APV_SUPPORT)
+ public static final int APVLevel6Band0 = 0x40001;
+ /** APV Codec Level 6, Band 1 as per IETF lim-apv-02, 10.1.4 */
+ @SuppressLint("AllUpper")
+ @FlaggedApi(FLAG_APV_SUPPORT)
+ public static final int APVLevel6Band1 = 0x40002;
+ /** APV Codec Level 6, Band 2 as per IETF lim-apv-02, 10.1.4 */
+ @SuppressLint("AllUpper")
+ @FlaggedApi(FLAG_APV_SUPPORT)
+ public static final int APVLevel6Band2 = 0x40004;
+ /** APV Codec Level 6, Band 3 as per IETF lim-apv-02, 10.1.4 */
+ @SuppressLint("AllUpper")
+ @FlaggedApi(FLAG_APV_SUPPORT)
+ public static final int APVLevel6Band3 = 0x40008;
+ /** APV Codec Level 6.1, Band 0 as per IETF lim-apv-02, 10.1.4 */
+ @SuppressLint("AllUpper")
+ @FlaggedApi(FLAG_APV_SUPPORT)
+ public static final int APVLevel61Band0 = 0x80001;
+ /** APV Codec Level 6.1, Band 1 as per IETF lim-apv-02, 10.1.4 */
+ @SuppressLint("AllUpper")
+ @FlaggedApi(FLAG_APV_SUPPORT)
+ public static final int APVLevel61Band1 = 0x80002;
+ /** APV Codec Level 6.1, Band 2 as per IETF lim-apv-02, 10.1.4 */
+ @SuppressLint("AllUpper")
+ @FlaggedApi(FLAG_APV_SUPPORT)
+ public static final int APVLevel61Band2 = 0x80004;
+ /** APV Codec Level 6.1, Band 3 as per IETF lim-apv-02, 10.1.4 */
+ @SuppressLint("AllUpper")
+ @FlaggedApi(FLAG_APV_SUPPORT)
+ public static final int APVLevel61Band3 = 0x80008;
+ /** APV Codec Level 7, Band 0 as per IETF lim-apv-02, 10.1.4 */
+ @SuppressLint("AllUpper")
+ @FlaggedApi(FLAG_APV_SUPPORT)
+ public static final int APVLevel7Band0 = 0x100001;
+ /** APV Codec Level 7, Band 1 as per IETF lim-apv-02, 10.1.4 */
+ @SuppressLint("AllUpper")
+ @FlaggedApi(FLAG_APV_SUPPORT)
+ public static final int APVLevel7Band1 = 0x100002;
+ /** APV Codec Level 7, Band 2 as per IETF lim-apv-02, 10.1.4 */
+ @SuppressLint("AllUpper")
+ @FlaggedApi(FLAG_APV_SUPPORT)
+ public static final int APVLevel7Band2 = 0x100004;
+ /** APV Codec Level 7, Band 3 as per IETF lim-apv-02, 10.1.4 */
+ @SuppressLint("AllUpper")
+ @FlaggedApi(FLAG_APV_SUPPORT)
+ public static final int APVLevel7Band3 = 0x100008;
+ /** APV Codec Level 7.1, Band 0 as per IETF lim-apv-02, 10.1.4 */
+ @SuppressLint("AllUpper")
+ @FlaggedApi(FLAG_APV_SUPPORT)
+ public static final int APVLevel71Band0 = 0x200001;
+ /** APV Codec Level 7.1, Band 1 as per IETF lim-apv-02, 10.1.4 */
+ @SuppressLint("AllUpper")
+ @FlaggedApi(FLAG_APV_SUPPORT)
+ public static final int APVLevel71Band1 = 0x200002;
+ /** APV Codec Level 7.1, Band 2 as per IETF lim-apv-02, 10.1.4 */
+ @SuppressLint("AllUpper")
+ @FlaggedApi(FLAG_APV_SUPPORT)
+ public static final int APVLevel71Band2 = 0x200004;
+ /** APV Codec Level 7.1, Band 3 as per IETF lim-apv-02, 10.1.4 */
+ @SuppressLint("AllUpper")
+ @FlaggedApi(FLAG_APV_SUPPORT)
+ public static final int APVLevel71Band3 = 0x200008;
+
/**
* The profile of the media content. Depending on the type of media this can be
* one of the profile values defined in this class.
diff --git a/media/java/android/media/MediaFormat.java b/media/java/android/media/MediaFormat.java
index cd0654ceb348..b08a86ee8f46 100644
--- a/media/java/android/media/MediaFormat.java
+++ b/media/java/android/media/MediaFormat.java
@@ -18,6 +18,7 @@ package android.media;
import static android.media.codec.Flags.FLAG_IN_PROCESS_SW_AUDIO_CODEC;
import static android.media.codec.Flags.FLAG_REGION_OF_INTEREST;
+import static android.media.codec.Flags.FLAG_APV_SUPPORT;
import static com.android.media.codec.flags.Flags.FLAG_CODEC_IMPORTANCE;
import static com.android.media.codec.flags.Flags.FLAG_LARGE_AUDIO_FRAME;
@@ -157,6 +158,8 @@ import java.util.stream.Collectors;
public final class MediaFormat {
public static final String MIMETYPE_VIDEO_VP8 = "video/x-vnd.on2.vp8";
public static final String MIMETYPE_VIDEO_VP9 = "video/x-vnd.on2.vp9";
+ @FlaggedApi(FLAG_APV_SUPPORT)
+ public static final String MIMETYPE_VIDEO_APV = "video/apv";
public static final String MIMETYPE_VIDEO_AV1 = "video/av01";
public static final String MIMETYPE_VIDEO_AVC = "video/avc";
public static final String MIMETYPE_VIDEO_HEVC = "video/hevc";
diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java
index bdfa63010adc..2d17bf500f12 100644
--- a/media/java/android/media/MediaRecorder.java
+++ b/media/java/android/media/MediaRecorder.java
@@ -387,13 +387,13 @@ public class MediaRecorder implements AudioRouting,
/**
* Audio source for capturing broadcast radio tuner output.
* Capturing the radio tuner output requires the
- * {@link android.Manifest.permission#CAPTURE_AUDIO_OUTPUT} permission.
+ * {@link android.Manifest.permission#CAPTURE_TUNER_AUDIO_INPUT} permission.
* This permission is reserved for use by system components and is not available to
* third-party applications.
* @hide
*/
@SystemApi
- @RequiresPermission(android.Manifest.permission.CAPTURE_AUDIO_OUTPUT)
+ @RequiresPermission(android.Manifest.permission.CAPTURE_TUNER_AUDIO_INPUT)
public static final int RADIO_TUNER = 1998;
/**
diff --git a/native/graphics/jni/Android.bp b/native/graphics/jni/Android.bp
index 0fb3049f63d8..23dd9b7aee37 100644
--- a/native/graphics/jni/Android.bp
+++ b/native/graphics/jni/Android.bp
@@ -48,7 +48,9 @@ cc_library_shared {
"libhwui_internal_headers",
],
- static_libs: ["libarect"],
+ static_libs: [
+ "libarect",
+ ],
host_supported: true,
target: {
@@ -60,6 +62,11 @@ cc_library_shared {
shared_libs: [
"libandroid",
],
+ static_libs: [
+ "libstatslog_hwui",
+ "libstatspull_lazy",
+ "libstatssocket_lazy",
+ ],
version_script: "libjnigraphics.map.txt",
},
host: {
diff --git a/native/graphics/jni/imagedecoder.cpp b/native/graphics/jni/imagedecoder.cpp
index e18b4a9d2420..cb95bcf5d2b1 100644
--- a/native/graphics/jni/imagedecoder.cpp
+++ b/native/graphics/jni/imagedecoder.cpp
@@ -14,18 +14,9 @@
* limitations under the License.
*/
-#include "aassetstreamadaptor.h"
-
-#include <android/asset_manager.h>
-#include <android/bitmap.h>
-#include <android/data_space.h>
-#include <android/imagedecoder.h>
#include <MimeType.h>
-#include <android/rect.h>
-#include <hwui/ImageDecoder.h>
-#include <log/log.h>
-#include <SkAndroidCodec.h>
#include <SkAlphaType.h>
+#include <SkAndroidCodec.h>
#include <SkCodec.h>
#include <SkCodecAnimation.h>
#include <SkColorSpace.h>
@@ -35,14 +26,24 @@
#include <SkRefCnt.h>
#include <SkSize.h>
#include <SkStream.h>
-#include <utils/Color.h>
-
+#include <android/asset_manager.h>
+#include <android/bitmap.h>
+#include <android/data_space.h>
+#include <android/imagedecoder.h>
+#include <android/rect.h>
#include <fcntl.h>
-#include <limits>
-#include <optional>
+#include <hwui/ImageDecoder.h>
+#include <log/log.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
+#include <utils/Color.h>
+#include <utils/StatsUtils.h>
+
+#include <limits>
+#include <optional>
+
+#include "aassetstreamadaptor.h"
using namespace android;
@@ -400,9 +401,7 @@ size_t AImageDecoder_getMinimumStride(AImageDecoder* decoder) {
return info.minRowBytes();
}
-int AImageDecoder_decodeImage(AImageDecoder* decoder,
- void* pixels, size_t stride,
- size_t size) {
+int AImageDecoder_decodeImage(AImageDecoder* decoder, void* pixels, size_t stride, size_t size) {
if (!decoder || !pixels || !stride) {
return ANDROID_IMAGE_DECODER_BAD_PARAMETER;
}
@@ -419,7 +418,13 @@ int AImageDecoder_decodeImage(AImageDecoder* decoder,
return ANDROID_IMAGE_DECODER_FINISHED;
}
- return ResultToErrorCode(imageDecoder->decode(pixels, stride));
+ const auto result = ResultToErrorCode(imageDecoder->decode(pixels, stride));
+
+ if (result == ANDROID_IMAGE_DECODER_SUCCESS) {
+ uirenderer::logBitmapDecode(imageDecoder->getOutputInfo(), false);
+ }
+
+ return result;
}
void AImageDecoder_delete(AImageDecoder* decoder) {
diff --git a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/HandlerExecutor.kt b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/HandlerExecutor.kt
index be086061f5cb..99d3c8db7716 100644
--- a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/HandlerExecutor.kt
+++ b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/HandlerExecutor.kt
@@ -24,7 +24,7 @@ import java.util.concurrent.Executor
* Adapter of [Handler] and [Executor], where the task is executed on handler with given looper.
*
* When current looper is same with the given looper, task passed to [Executor.execute] will be
- * executed immediately to improve better performance.
+ * executed immediately to achieve better performance.
*
* @param looper Looper of the handler.
*/
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 9cb0ebb995a8..843d2aadf333 100644
--- a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/KeyedObserver.kt
+++ b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/KeyedObserver.kt
@@ -114,6 +114,10 @@ interface KeyedObservable<K> {
fun notifyChange(key: K, reason: Int)
}
+/** Delegation of [KeyedObservable]. */
+open class KeyedObservableDelegate<K>(delegate: KeyedObservable<K>) :
+ KeyedObservable<K> by delegate
+
/** A thread safe implementation of [KeyedObservable]. */
open class KeyedDataObservable<K> : KeyedObservable<K> {
// Instead of @GuardedBy("this"), guarded by itself because KeyedDataObservable object could be
diff --git a/packages/SettingsLib/Graph/graph.proto b/packages/SettingsLib/Graph/graph.proto
index e93d756d0285..cbe602e00406 100644
--- a/packages/SettingsLib/Graph/graph.proto
+++ b/packages/SettingsLib/Graph/graph.proto
@@ -74,6 +74,9 @@ message PreferenceProto {
optional ActionTarget action_target = 12;
// Preference value (if present, it means `persistent` is true).
optional PreferenceValueProto value = 13;
+ // Intent to show and locate the preference (might have highlight animation on
+ // the preference).
+ optional IntentProto launch_intent = 14;
// Target of an Intent
message ActionTarget {
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 5e78a290179e..9cb872a797db 100644
--- a/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceGraphBuilder.kt
+++ b/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceGraphBuilder.kt
@@ -51,9 +51,9 @@ import com.android.settingslib.metadata.PreferenceSummaryProvider
import com.android.settingslib.metadata.PreferenceTitleProvider
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"
@@ -140,7 +140,7 @@ private constructor(private val context: Context, private val request: GetPrefer
addPreferenceScreen(metadata.key) {
preferenceScreenProto {
completeHierarchy = metadata.hasCompleteHierarchy()
- root = metadata.getPreferenceHierarchy(context).toProto(true)
+ root = metadata.getPreferenceHierarchy(context).toProto(metadata, true)
}
}
@@ -237,23 +237,29 @@ private constructor(private val context: Context, private val request: GetPrefer
this@toProto.intent?.let { actionTarget = it.toActionTarget() }
}
- private suspend fun PreferenceHierarchy.toProto(isRoot: Boolean): PreferenceGroupProto =
- preferenceGroupProto {
- preference = toProto(this@toProto, isRoot)
- forEachAsync {
- addPreferences(
- preferenceOrGroupProto {
- if (it is PreferenceHierarchy) {
- group = it.toProto(false)
- } else {
- preference = toProto(it, false)
- }
+ private suspend fun PreferenceHierarchy.toProto(
+ screenMetadata: PreferenceScreenMetadata,
+ isRoot: Boolean,
+ ): PreferenceGroupProto = preferenceGroupProto {
+ preference = toProto(screenMetadata, this@toProto, isRoot)
+ forEachAsync {
+ addPreferences(
+ preferenceOrGroupProto {
+ if (it is PreferenceHierarchy) {
+ group = it.toProto(screenMetadata, false)
+ } else {
+ preference = toProto(screenMetadata, it, false)
}
- )
- }
+ }
+ )
}
+ }
- private suspend fun toProto(node: PreferenceHierarchyNode, isRoot: Boolean) = preferenceProto {
+ private suspend fun toProto(
+ screenMetadata: PreferenceScreenMetadata,
+ node: PreferenceHierarchyNode,
+ isRoot: Boolean,
+ ) = preferenceProto {
val metadata = node.metadata
key = metadata.key
metadata.getTitleTextProto(isRoot)?.let { title = it }
@@ -291,6 +297,7 @@ private constructor(private val context: Context, private val request: GetPrefer
@Suppress("CheckReturnValue") addPreferenceScreenMetadata(metadata)
}
metadata.intent(context)?.let { actionTarget = it.toActionTarget() }
+ screenMetadata.getLaunchIntent(context, metadata)?.let { launchIntent = it.toProto() }
}
private fun PreferenceMetadata.getTitleTextProto(isRoot: Boolean): TextProto? {
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 10087a712bcf..386b6d94c631 100644
--- a/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceMetadata.kt
+++ b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceMetadata.kt
@@ -162,8 +162,8 @@ interface PreferenceMetadata {
/**
* Returns the preference icon.
*
- * Implement [PreferenceIconProvider] interface if icon content is provided dynamically
- * (e.g. icon is provided based on flag value).
+ * Implement [PreferenceIconProvider] interface if icon is provided dynamically (e.g. icon is
+ * provided based on flag value).
*/
fun getPreferenceIcon(context: Context): Int =
when {
@@ -212,4 +212,11 @@ interface PreferenceScreenMetadata : PreferenceMetadata {
* conditions. DO NOT check any condition (except compile time flag) before adding a preference.
*/
fun getPreferenceHierarchy(context: Context): PreferenceHierarchy
+
+ /**
+ * Returns the [Intent] to show current preference screen.
+ *
+ * @param metadata the preference to locate when show the screen
+ */
+ fun getLaunchIntent(context: Context, metadata: PreferenceMetadata?): Intent? = null
}
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 7af9c81a61c1..d75f3e84274d 100644
--- a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceScreenBindingHelper.kt
+++ b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceScreenBindingHelper.kt
@@ -237,7 +237,8 @@ class PreferenceScreenBindingHelper(
} else {
preferences[preference.key]?.let {
preferenceBindingFactory.bind(preference, it)
- (it as? PersistentPreference<*>)?.storage(context)?.let { storage ->
+ val metadata = it.metadata
+ (metadata as? PersistentPreference<*>)?.storage(context)?.let { storage ->
preference.preferenceDataStore =
storages.getOrPut(storage) { PreferenceDataStoreAdapter(storage) }
}
diff --git a/packages/SettingsLib/res/values-bs/strings.xml b/packages/SettingsLib/res/values-bs/strings.xml
index b76b028ac4b9..4d334660895e 100644
--- a/packages/SettingsLib/res/values-bs/strings.xml
+++ b/packages/SettingsLib/res/values-bs/strings.xml
@@ -82,7 +82,7 @@
<string name="speed_label_very_fast" msgid="8215718029533182439">"Veoma brzo"</string>
<string name="wifi_passpoint_expired" msgid="6540867261754427561">"Isteklo"</string>
<string name="preference_summary_default_combination" msgid="2644094566845577901">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="DESCRIPTION">%2$s</xliff:g>"</string>
- <string name="bluetooth_disconnected" msgid="7739366554710388701">"Isključen"</string>
+ <string name="bluetooth_disconnected" msgid="7739366554710388701">"Nije povezano"</string>
<string name="bluetooth_disconnecting" msgid="7638892134401574338">"Prekidanje veze…"</string>
<string name="bluetooth_connecting" msgid="5871702668260192755">"Povezivanje…"</string>
<string name="bluetooth_connected" msgid="8065345572198502293">"Povezano<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-in/arrays.xml b/packages/SettingsLib/res/values-in/arrays.xml
index 038800cf4bfa..00ea04d68c16 100644
--- a/packages/SettingsLib/res/values-in/arrays.xml
+++ b/packages/SettingsLib/res/values-in/arrays.xml
@@ -29,7 +29,7 @@
<item msgid="4613015005934755724">"Terhubung"</item>
<item msgid="3763530049995655072">"Ditangguhkan"</item>
<item msgid="7852381437933824454">"Memutus sambungan..."</item>
- <item msgid="5046795712175415059">"Sambungan terputus"</item>
+ <item msgid="5046795712175415059">"Tidak terhubung"</item>
<item msgid="2473654476624070462">"Gagal"</item>
<item msgid="9146847076036105115">"Diblokir"</item>
<item msgid="4543924085816294893">"Menghindari sambungan buruk untuk sementara"</item>
@@ -43,7 +43,7 @@
<item msgid="1043944043827424501">"Terhubung ke <xliff:g id="NETWORK_NAME">%1$s</xliff:g>"</item>
<item msgid="7445993821842009653">"Ditangguhkan"</item>
<item msgid="1175040558087735707">"Diputus dari <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
- <item msgid="699832486578171722">"Sambungan terputus"</item>
+ <item msgid="699832486578171722">"Tidak terhubung"</item>
<item msgid="522383512264986901">"Gagal"</item>
<item msgid="3602596701217484364">"Diblokir"</item>
<item msgid="1999413958589971747">"Menghindari sambungan buruk untuk sementara"</item>
diff --git a/packages/SettingsLib/res/values-in/strings.xml b/packages/SettingsLib/res/values-in/strings.xml
index 3b265f8e9d38..bfd8f397be60 100644
--- a/packages/SettingsLib/res/values-in/strings.xml
+++ b/packages/SettingsLib/res/values-in/strings.xml
@@ -50,7 +50,7 @@
<string name="wifi_security_owe" msgid="3343421403561657809">"Enhanced Open"</string>
<string name="wifi_security_eap_suiteb" msgid="415842785991698142">"WPA3-Enterprise 192-bit"</string>
<string name="wifi_remembered" msgid="3266709779723179188">"Disimpan"</string>
- <string name="wifi_disconnected" msgid="7054450256284661757">"Terputus"</string>
+ <string name="wifi_disconnected" msgid="7054450256284661757">"Tidak terhubung"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"Nonaktif"</string>
<string name="wifi_disabled_network_failure" msgid="2660396183242399585">"Kegagalan Konfigurasi IP"</string>
<string name="wifi_disabled_password_failure" msgid="6892387079613226738">"Masalah autentikasi"</string>
@@ -82,7 +82,7 @@
<string name="speed_label_very_fast" msgid="8215718029533182439">"Sangat Cepat"</string>
<string name="wifi_passpoint_expired" msgid="6540867261754427561">"Sudah tidak berlaku"</string>
<string name="preference_summary_default_combination" msgid="2644094566845577901">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="DESCRIPTION">%2$s</xliff:g>"</string>
- <string name="bluetooth_disconnected" msgid="7739366554710388701">"Sambungan terputus"</string>
+ <string name="bluetooth_disconnected" msgid="7739366554710388701">"Tidak terhubung"</string>
<string name="bluetooth_disconnecting" msgid="7638892134401574338">"Memutus sambungan..."</string>
<string name="bluetooth_connecting" msgid="5871702668260192755">"Menghubungkan…"</string>
<string name="bluetooth_connected" msgid="8065345572198502293">"Terhubung<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-ky/strings.xml b/packages/SettingsLib/res/values-ky/strings.xml
index 2fb036f8356c..785bf43bb4d1 100644
--- a/packages/SettingsLib/res/values-ky/strings.xml
+++ b/packages/SettingsLib/res/values-ky/strings.xml
@@ -258,7 +258,7 @@
<string name="adb_pair_method_qrcode_summary" msgid="7130694277228970888">"QR кодунун сканерин колдонуп, жаңы түзмөктөрдү жупташтырыңыз"</string>
<string name="adb_pair_method_code_title" msgid="1122590300445142904">"Түзмөктү атайын код аркылуу жупташтыруу"</string>
<string name="adb_pair_method_code_summary" msgid="6370414511333685185">"Жаңы түзмөктөрдү алты сандан турган код аркылуу жупташтырасыз"</string>
- <string name="adb_paired_devices_title" msgid="5268997341526217362">"Жупташтырылган түзмөктөр"</string>
+ <string name="adb_paired_devices_title" msgid="5268997341526217362">"Байланышкан түзмөктөр"</string>
<string name="adb_wireless_device_connected_summary" msgid="3039660790249148713">"Учурда туташып турган түзмөктөр"</string>
<string name="adb_wireless_device_details_title" msgid="7129369670526565786">"Түзмөктүн чоо-жайы"</string>
<string name="adb_device_forget" msgid="193072400783068417">"Унутулсун"</string>
diff --git a/packages/SettingsLib/res/values-mk/strings.xml b/packages/SettingsLib/res/values-mk/strings.xml
index f5e84a23aa07..b9d970d8388f 100644
--- a/packages/SettingsLib/res/values-mk/strings.xml
+++ b/packages/SettingsLib/res/values-mk/strings.xml
@@ -85,7 +85,7 @@
<string name="bluetooth_disconnected" msgid="7739366554710388701">"Не е поврзано"</string>
<string name="bluetooth_disconnecting" msgid="7638892134401574338">"Се исклучува..."</string>
<string name="bluetooth_connecting" msgid="5871702668260192755">"Се поврзува..."</string>
- <string name="bluetooth_connected" msgid="8065345572198502293">"Поврзан со <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_connected" msgid="8065345572198502293">"Поврзано<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_pairing" msgid="4269046942588193600">"Се спарува..."</string>
<string name="bluetooth_connected_no_headset" msgid="2224101138659967604">"Поврзан со <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> (без телефон)"</string>
<string name="bluetooth_connected_no_a2dp" msgid="8566874395813947092">"Поврзан со <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> (без аудиовизуелни содржини)"</string>
diff --git a/packages/SettingsLib/res/values-or/strings.xml b/packages/SettingsLib/res/values-or/strings.xml
index 73b099695da8..ae1df210bce3 100644
--- a/packages/SettingsLib/res/values-or/strings.xml
+++ b/packages/SettingsLib/res/values-or/strings.xml
@@ -82,7 +82,7 @@
<string name="speed_label_very_fast" msgid="8215718029533182439">"ଅତି ଦ୍ରୁତ"</string>
<string name="wifi_passpoint_expired" msgid="6540867261754427561">"ମିଆଦ ଶେଷ ହୋଇଯାଇଛି"</string>
<string name="preference_summary_default_combination" msgid="2644094566845577901">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="DESCRIPTION">%2$s</xliff:g>"</string>
- <string name="bluetooth_disconnected" msgid="7739366554710388701">"ବିଛିନ୍ନ ହେଲା"</string>
+ <string name="bluetooth_disconnected" msgid="7739366554710388701">"ଡିସକନେକ୍ଟ ହୋଇଛି"</string>
<string name="bluetooth_disconnecting" msgid="7638892134401574338">"ବିଚ୍ଛିନ୍ନ କରୁଛି…"</string>
<string name="bluetooth_connecting" msgid="5871702668260192755">"କନେକ୍ଟ ହେଉଛି…"</string>
<string name="bluetooth_connected" msgid="8065345572198502293">"ସଂଯୁକ୍ତ ହେଲା<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-sk/strings.xml b/packages/SettingsLib/res/values-sk/strings.xml
index 74c294b84db7..4c41b7d4e397 100644
--- a/packages/SettingsLib/res/values-sk/strings.xml
+++ b/packages/SettingsLib/res/values-sk/strings.xml
@@ -82,7 +82,7 @@
<string name="speed_label_very_fast" msgid="8215718029533182439">"Veľmi rýchla"</string>
<string name="wifi_passpoint_expired" msgid="6540867261754427561">"Vypršalo"</string>
<string name="preference_summary_default_combination" msgid="2644094566845577901">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="DESCRIPTION">%2$s</xliff:g>"</string>
- <string name="bluetooth_disconnected" msgid="7739366554710388701">"Odpojený"</string>
+ <string name="bluetooth_disconnected" msgid="7739366554710388701">"Odpojené"</string>
<string name="bluetooth_disconnecting" msgid="7638892134401574338">"Prebieha odpájanie..."</string>
<string name="bluetooth_connecting" msgid="5871702668260192755">"Prebieha pripájanie…"</string>
<string name="bluetooth_connected" msgid="8065345572198502293">"Pripojené k zariadeniu <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml
index 1c1117d39d5b..1d7f62ac6c93 100644
--- a/packages/SettingsLib/res/values-sv/strings.xml
+++ b/packages/SettingsLib/res/values-sv/strings.xml
@@ -82,7 +82,7 @@
<string name="speed_label_very_fast" msgid="8215718029533182439">"Mycket snabb"</string>
<string name="wifi_passpoint_expired" msgid="6540867261754427561">"Har upphört att gälla"</string>
<string name="preference_summary_default_combination" msgid="2644094566845577901">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="DESCRIPTION">%2$s</xliff:g>"</string>
- <string name="bluetooth_disconnected" msgid="7739366554710388701">"Kopplas ifrån"</string>
+ <string name="bluetooth_disconnected" msgid="7739366554710388701">"Frånkopplad"</string>
<string name="bluetooth_disconnecting" msgid="7638892134401574338">"Kopplar ifrån…"</string>
<string name="bluetooth_connecting" msgid="5871702668260192755">"Ansluter…"</string>
<string name="bluetooth_connected" msgid="8065345572198502293">"Ansluten<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml
index 57b8f5914e71..060b7b267624 100644
--- a/packages/SettingsLib/res/values-th/strings.xml
+++ b/packages/SettingsLib/res/values-th/strings.xml
@@ -82,7 +82,7 @@
<string name="speed_label_very_fast" msgid="8215718029533182439">"เร็วมาก"</string>
<string name="wifi_passpoint_expired" msgid="6540867261754427561">"หมดอายุแล้ว"</string>
<string name="preference_summary_default_combination" msgid="2644094566845577901">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="DESCRIPTION">%2$s</xliff:g>"</string>
- <string name="bluetooth_disconnected" msgid="7739366554710388701">"ตัดการเชื่อมต่อ"</string>
+ <string name="bluetooth_disconnected" msgid="7739366554710388701">"ยกเลิกการเชื่อมต่อแล้ว"</string>
<string name="bluetooth_disconnecting" msgid="7638892134401574338">"กำลังตัดการเชื่อมต่อ..."</string>
<string name="bluetooth_connecting" msgid="5871702668260192755">"กำลังเชื่อมต่อ…"</string>
<string name="bluetooth_connected" msgid="8065345572198502293">"เชื่อมต่อแล้ว<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingId.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingId.java
index e7c7476d4797..f4b79db67d88 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingId.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingId.java
@@ -120,4 +120,10 @@ public @interface DeviceSettingId {
/** Device setting ID for ANC. */
int DEVICE_SETTING_ID_ANC = 1001;
+
+ /** Device setting expandable ID 1. */
+ int DEVICE_SETTING_ID_EXPANDABLE_1 = 3001;
+
+ /** Device setting expandable ID 2. */
+ int DEVICE_SETTING_ID_EXPANDABLE_2 = 3100;
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingRepository.kt b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingRepository.kt
index 8537897cb329..c68dbeead26b 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingRepository.kt
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingRepository.kt
@@ -106,26 +106,40 @@ class DeviceSettingRepositoryImpl(
private fun DeviceSettingsConfig.toModel(): DeviceSettingConfigModel =
DeviceSettingConfigModel(
- mainItems = mainContentItems.map { it.toModel() },
- moreSettingsItems = moreSettingsItems.map { it.toModel() },
+ mainItems = mainContentItems.toModel(),
+ moreSettingsItems = moreSettingsItems.toModel(),
moreSettingsHelpItem = moreSettingsHelpItem?.toModel(),
)
- private fun DeviceSettingItem.toModel(): DeviceSettingConfigItemModel {
+ private fun List<DeviceSettingItem>.toModel(): List<DeviceSettingConfigItemModel> {
+ return this.flatMap { item ->
+ if (item.settingId in EXPANDABLE_SETTING_IDS) {
+ IntRange(item.settingId, item.settingId + SETTING_ID_EXPAND_LIMIT - 1).map {
+ item.toModel(overrideSettingId = it)
+ }
+ } else {
+ listOf(item.toModel())
+ }
+ }
+ }
+
+ private fun DeviceSettingItem.toModel(
+ overrideSettingId: Int? = null
+ ): DeviceSettingConfigItemModel {
return if (!TextUtils.isEmpty(preferenceKey)) {
if (settingId == DeviceSettingId.DEVICE_SETTING_ID_BLUETOOTH_PROFILES) {
BluetoothProfilesItem(
- settingId,
+ overrideSettingId ?: settingId,
highlighted,
preferenceKey!!,
extras.getStringArrayList(DeviceSettingContract.INVISIBLE_PROFILES)
?: emptyList(),
)
} else {
- CommonBuiltinItem(settingId, highlighted, preferenceKey!!)
+ CommonBuiltinItem(overrideSettingId ?: settingId, highlighted, preferenceKey!!)
}
} else {
- AppProvidedItem(settingId, highlighted)
+ AppProvidedItem(overrideSettingId ?: settingId, highlighted)
}
}
@@ -134,6 +148,7 @@ class DeviceSettingRepositoryImpl(
is DeviceSettingIntentAction -> DeviceSettingActionModel.IntentAction(this.intent)
is DeviceSettingPendingIntentAction ->
DeviceSettingActionModel.PendingIntentAction(this.pendingIntent)
+
else -> null
}
@@ -150,7 +165,7 @@ class DeviceSettingRepositoryImpl(
summary = pref.summary,
icon = pref.icon?.let { DeviceSettingIcon.BitmapIcon(it) },
isAllowedChangingState = pref.isAllowedChangingState,
- action = pref.action?.toModel(),
+ action = pref.action.toModel(),
switchState =
if (pref.hasSwitch()) {
DeviceSettingStateModel.ActionSwitchPreferenceState(pref.checked)
@@ -163,6 +178,7 @@ class DeviceSettingRepositoryImpl(
}
},
)
+
is MultiTogglePreference ->
DeviceSettingModel.MultiTogglePreference(
cachedDevice = cachedDevice,
@@ -178,21 +194,33 @@ class DeviceSettingRepositoryImpl(
}
},
)
+
is DeviceSettingFooterPreference ->
DeviceSettingModel.FooterPreference(
cachedDevice = cachedDevice,
id = settingId,
footerText = pref.footerText,
)
+
is DeviceSettingHelpPreference ->
DeviceSettingModel.HelpPreference(
cachedDevice = cachedDevice,
id = settingId,
intent = pref.intent,
)
+
else -> DeviceSettingModel.Unknown(cachedDevice, settingId)
}
private fun ToggleInfo.toModel(): ToggleModel =
ToggleModel(label, DeviceSettingIcon.BitmapIcon(icon))
+
+ companion object {
+ private val EXPANDABLE_SETTING_IDS =
+ listOf(
+ DeviceSettingId.DEVICE_SETTING_ID_EXPANDABLE_1,
+ DeviceSettingId.DEVICE_SETTING_ID_EXPANDABLE_2,
+ )
+ private const val SETTING_ID_EXPAND_LIMIT = 15
+ }
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingRepositoryTest.kt b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingRepositoryTest.kt
index 4e62fd3b27c5..3d4e4492ffd1 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingRepositoryTest.kt
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingRepositoryTest.kt
@@ -167,6 +167,26 @@ class DeviceSettingRepositoryTest {
}
@Test
+ fun getDeviceSettingsConfig_expandable_success() {
+ testScope.runTest {
+ setUpConfigService(true, DEVICE_SETTING_CONFIG_EXPANDABLE)
+ `when`(settingProviderService1.serviceStatus)
+ .thenReturn(DeviceSettingsProviderServiceStatus(true))
+ `when`(settingProviderService2.serviceStatus)
+ .thenReturn(DeviceSettingsProviderServiceStatus(true))
+
+ val config = underTest.getDeviceSettingsConfig(cachedDevice)!!
+
+ assertThat(config.mainItems.map { it.settingId }).isEqualTo(
+ IntRange(
+ DeviceSettingId.DEVICE_SETTING_ID_EXPANDABLE_1,
+ DeviceSettingId.DEVICE_SETTING_ID_EXPANDABLE_1 + 14
+ ).toList()
+ )
+ }
+ }
+
+ @Test
fun getDeviceSettingsConfig_noMetadata_returnNull() {
testScope.runTest {
`when`(
@@ -510,6 +530,13 @@ class DeviceSettingRepositoryTest {
SETTING_PROVIDER_SERVICE_CLASS_NAME_2,
SETTING_PROVIDER_SERVICE_INTENT_ACTION_2,
)
+ val DEVICE_SETTING_APP_PROVIDED_ITEM_EXPANDABLE =
+ DeviceSettingItem(
+ DeviceSettingId.DEVICE_SETTING_ID_EXPANDABLE_1,
+ SETTING_PROVIDER_SERVICE_PACKAGE_NAME_1,
+ SETTING_PROVIDER_SERVICE_CLASS_NAME_1,
+ SETTING_PROVIDER_SERVICE_INTENT_ACTION_1,
+ )
val DEVICE_SETTING_BUILT_IN_ITEM =
DeviceSettingItem(
DeviceSettingId.DEVICE_SETTING_ID_BLUETOOTH_AUDIO_DEVICE_TYPE_GROUP,
@@ -581,5 +608,13 @@ class DeviceSettingRepositoryTest {
listOf(DEVICE_SETTING_APP_PROVIDED_ITEM_2),
DEVICE_SETTING_HELP_ITEM,
)
+ val DEVICE_SETTING_CONFIG_EXPANDABLE =
+ DeviceSettingsConfig(
+ listOf(
+ DEVICE_SETTING_APP_PROVIDED_ITEM_EXPANDABLE,
+ ),
+ listOf(),
+ DEVICE_SETTING_HELP_ITEM,
+ )
}
}
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
index 509b88b257fe..558ccaf7a707 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
@@ -263,5 +263,6 @@ public class SystemSettingsValidators {
VALIDATORS.put(System.NOTIFICATION_COOLDOWN_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(System.NOTIFICATION_COOLDOWN_ALL, BOOLEAN_VALIDATOR);
VALIDATORS.put(System.NOTIFICATION_COOLDOWN_VIBRATE_UNLOCKED, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(System.HEARING_DEVICE_LOCAL_AMBIENT_VOLUME, ANY_STRING_VALIDATOR);
}
}
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index 1659c9eb67f2..c2beaa8048f6 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -944,7 +944,8 @@ public class SettingsBackupTest {
Settings.System.WEAR_ACCESSIBILITY_GESTURE_ENABLED_DURING_OOBE,
Settings.System.WEAR_TTS_PREWARM_ENABLED,
Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ,
- Settings.System.MULTI_AUDIO_FOCUS_ENABLED // form-factor/OEM specific
+ Settings.System.MULTI_AUDIO_FOCUS_ENABLED, // form-factor/OEM specific
+ Settings.System.HEARING_DEVICE_LOCAL_AMBIENT_VOLUME // internal cache
);
if (!Flags.backUpSmoothDisplayAndForcePeakRefreshRate()) {
settings.add(Settings.System.MIN_REFRESH_RATE);
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 0537b1454cea..f0e1b437ec51 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -1016,6 +1016,10 @@
android:exported="false">
</activity>
+ <service
+ android:name="com.android.systemui.communal.widgets.GlanceableHubWidgetManagerService"
+ android:exported="false" />
+
<!-- Doze with notifications, run in main sysui process for every user -->
<service
android:name=".doze.DozeService"
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index c4032c5ca2ec..83b75664e1a6 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -748,10 +748,13 @@ flag {
}
flag {
- name: "smartspace_swipe_event_logging"
+ name: "smartspace_swipe_event_logging_fix"
namespace: "systemui"
description: "Log card swipe events in smartspace"
bug: "374150422"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
}
flag {
@@ -1151,6 +1154,13 @@ flag {
}
flag {
+ name: "communal_hub_on_mobile"
+ namespace: "systemui"
+ description: "Brings the glanceable hub experience to mobile phones"
+ bug: "375689917"
+}
+
+flag {
name: "dream_overlay_updated_font"
namespace: "systemui"
description: "Flag to enable updated font settings for dream overlay"
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 9d4408aa7acd..46f5ecd99301 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
@@ -121,14 +121,15 @@ private fun Modifier.panelPadding(): Modifier {
val systemBars = WindowInsets.systemBarsIgnoringVisibility
val displayCutout = WindowInsets.displayCutout
val waterfall = WindowInsets.waterfall
- val contentPadding = PaddingValues(all = OverlayShade.Dimensions.ScrimContentPadding)
+ val horizontalPadding =
+ PaddingValues(horizontal = dimensionResource(id = R.dimen.shade_panel_margin_horizontal))
val combinedPadding =
combinePaddings(
systemBars.asPaddingValues(),
displayCutout.asPaddingValues(),
waterfall.asPaddingValues(),
- contentPadding,
+ horizontalPadding,
)
return if (widthSizeClass == WindowWidthSizeClass.Compact) {
@@ -174,7 +175,6 @@ object OverlayShade {
}
object Dimensions {
- val ScrimContentPadding = 16.dp
val PanelCornerRadius = 46.dp
val OverscrollLimit = 32.dp
}
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 f14622fe7b65..63c5d7aed3e1 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
@@ -48,7 +48,6 @@ import androidx.compose.ui.unit.round
import androidx.compose.ui.util.fastCoerceIn
import androidx.compose.ui.util.fastForEachReversed
import androidx.compose.ui.util.lerp
-import com.android.compose.animation.scene.Element.State
import com.android.compose.animation.scene.content.Content
import com.android.compose.animation.scene.content.state.TransitionState
import com.android.compose.animation.scene.transformation.PropertyTransformation
@@ -163,7 +162,7 @@ private fun Modifier.maybeElevateInContent(
transitionStates: List<TransitionState>,
): Modifier {
fun isSharedElement(
- stateByContent: Map<ContentKey, State>,
+ stateByContent: Map<ContentKey, Element.State>,
transition: TransitionState.Transition,
): Boolean {
fun inFromContent() = transition.fromContent in stateByContent
@@ -1281,14 +1280,14 @@ private inline fun <T> computeValue(
checkNotNull(if (currentContent == toContent) toState else fromState)
val idleValue = contentValue(overscrollState)
val targetValue =
- propertySpec.transform(
- layoutImpl,
- currentContent,
- element,
- overscrollState,
- transition,
- idleValue,
- )
+ with(propertySpec) {
+ layoutImpl.propertyTransformationScope.transform(
+ currentContent,
+ element.key,
+ transition,
+ idleValue,
+ )
+ }
// Make sure we don't read progress if values are the same and we don't need to
// interpolate, so we don't invalidate the phase where this is read.
@@ -1376,24 +1375,26 @@ private inline fun <T> computeValue(
val idleValue = contentValue(contentState)
val isEntering = content == toContent
val previewTargetValue =
- previewTransformation.transform(
- layoutImpl,
- content,
- element,
- contentState,
- transition,
- idleValue,
- )
+ with(previewTransformation) {
+ layoutImpl.propertyTransformationScope.transform(
+ content,
+ element.key,
+ transition,
+ idleValue,
+ )
+ }
val targetValueOrNull =
- transformation?.transform(
- layoutImpl,
- content,
- element,
- contentState,
- transition,
- idleValue,
- )
+ transformation?.let { transformation ->
+ with(transformation) {
+ layoutImpl.propertyTransformationScope.transform(
+ content,
+ element.key,
+ transition,
+ idleValue,
+ )
+ }
+ }
// Make sure we don't read progress if values are the same and we don't need to interpolate,
// so we don't invalidate the phase where this is read.
@@ -1460,7 +1461,14 @@ private inline fun <T> computeValue(
val idleValue = contentValue(contentState)
val targetValue =
- transformation.transform(layoutImpl, content, element, contentState, transition, idleValue)
+ with(transformation) {
+ layoutImpl.propertyTransformationScope.transform(
+ content,
+ element.key,
+ transition,
+ idleValue,
+ )
+ }
// Make sure we don't read progress if values are the same and we don't need to interpolate, so
// we don't invalidate the phase where this is read.
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/PredictiveBackHandler.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/PredictiveBackHandler.kt
index b00c8ade07eb..8a6a0d6dbb99 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/PredictiveBackHandler.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/PredictiveBackHandler.kt
@@ -70,6 +70,7 @@ internal fun PredictiveBackHandler(
// The predictive back APIs will automatically animate the progress for us in this case
// so there is no need to animate it.
cancelSpec = snap(),
+ animationScope = layoutImpl.animationScope,
)
}
}
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 d58d3f24862f..e93cf8f714cd 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
@@ -129,6 +129,7 @@ internal class SceneTransitionLayoutImpl(
private val verticalDraggableHandler: DraggableHandlerImpl
internal val elementStateScope = ElementStateScopeImpl(this)
+ internal val propertyTransformationScope = PropertyTransformationScopeImpl(this)
private var _userActionDistanceScope: UserActionDistanceScope? = null
internal val userActionDistanceScope: UserActionDistanceScope
get() =
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/UserActionDistanceScopeImpl.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/UserActionDistanceScopeImpl.kt
index 690c809aa56d..8457481b3e14 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/UserActionDistanceScopeImpl.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/UserActionDistanceScopeImpl.kt
@@ -18,6 +18,8 @@ package com.android.compose.animation.scene
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.unit.IntSize
+import androidx.compose.ui.unit.LayoutDirection
+import com.android.compose.animation.scene.transformation.PropertyTransformationScope
internal class ElementStateScopeImpl(private val layoutImpl: SceneTransitionLayoutImpl) :
ElementStateScope {
@@ -46,3 +48,15 @@ internal class UserActionDistanceScopeImpl(private val layoutImpl: SceneTransiti
override val fontScale: Float
get() = layoutImpl.density.fontScale
}
+
+internal class PropertyTransformationScopeImpl(private val layoutImpl: SceneTransitionLayoutImpl) :
+ PropertyTransformationScope, ElementStateScope by layoutImpl.elementStateScope {
+ override val density: Float
+ get() = layoutImpl.density.density
+
+ override val fontScale: Float
+ get() = layoutImpl.density.fontScale
+
+ override val layoutDirection: LayoutDirection
+ get() = layoutImpl.layoutDirection
+}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/AnchoredSize.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/AnchoredSize.kt
index c5a3067cc4d9..5936d2595465 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/AnchoredSize.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/AnchoredSize.kt
@@ -18,10 +18,8 @@ package com.android.compose.animation.scene.transformation
import androidx.compose.ui.unit.IntSize
import com.android.compose.animation.scene.ContentKey
-import com.android.compose.animation.scene.Element
import com.android.compose.animation.scene.ElementKey
import com.android.compose.animation.scene.ElementMatcher
-import com.android.compose.animation.scene.SceneTransitionLayoutImpl
import com.android.compose.animation.scene.content.state.TransitionState
/** Anchor the size of an element to the size of another element. */
@@ -31,19 +29,15 @@ internal class AnchoredSize(
private val anchorWidth: Boolean,
private val anchorHeight: Boolean,
) : PropertyTransformation<IntSize> {
- override fun transform(
- layoutImpl: SceneTransitionLayoutImpl,
+ override fun PropertyTransformationScope.transform(
content: ContentKey,
- element: Element,
- stateInContent: Element.State,
+ element: ElementKey,
transition: TransitionState.Transition,
value: IntSize,
): IntSize {
fun anchorSizeIn(content: ContentKey): IntSize {
val size =
- layoutImpl.elements[anchor]?.stateByContent?.get(content)?.targetSize?.takeIf {
- it != Element.SizeUnspecified
- }
+ anchor.targetSize(content)
?: throwMissingAnchorException(
transformation = "AnchoredSize",
anchor = anchor,
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/AnchoredTranslate.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/AnchoredTranslate.kt
index 86e06ab1f243..0a59dfe515fc 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/AnchoredTranslate.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/AnchoredTranslate.kt
@@ -17,12 +17,9 @@
package com.android.compose.animation.scene.transformation
import androidx.compose.ui.geometry.Offset
-import androidx.compose.ui.geometry.isSpecified
import com.android.compose.animation.scene.ContentKey
-import com.android.compose.animation.scene.Element
import com.android.compose.animation.scene.ElementKey
import com.android.compose.animation.scene.ElementMatcher
-import com.android.compose.animation.scene.SceneTransitionLayoutImpl
import com.android.compose.animation.scene.content.state.TransitionState
/** Anchor the translation of an element to another element. */
@@ -30,11 +27,9 @@ internal class AnchoredTranslate(
override val matcher: ElementMatcher,
private val anchor: ElementKey,
) : PropertyTransformation<Offset> {
- override fun transform(
- layoutImpl: SceneTransitionLayoutImpl,
+ override fun PropertyTransformationScope.transform(
content: ContentKey,
- element: Element,
- stateInContent: Element.State,
+ element: ElementKey,
transition: TransitionState.Transition,
value: Offset,
): Offset {
@@ -46,18 +41,13 @@ internal class AnchoredTranslate(
)
}
- val anchor = layoutImpl.elements[anchor] ?: throwException(content = null)
- fun anchorOffsetIn(content: ContentKey): Offset? {
- return anchor.stateByContent[content]?.targetOffset?.takeIf { it.isSpecified }
- }
-
// [element] will move the same amount as [anchor] does.
// TODO(b/290184746): Also support anchors that are not shared but translated because of
// other transformations, like an edge translation.
val anchorFromOffset =
- anchorOffsetIn(transition.fromContent) ?: throwException(transition.fromContent)
+ anchor.targetOffset(transition.fromContent) ?: throwException(transition.fromContent)
val anchorToOffset =
- anchorOffsetIn(transition.toContent) ?: throwException(transition.toContent)
+ anchor.targetOffset(transition.toContent) ?: throwException(transition.toContent)
val offset = anchorToOffset - anchorFromOffset
return if (content == transition.toContent) {
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/DrawScale.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/DrawScale.kt
index 7f8647976873..7223dad43a2e 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/DrawScale.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/DrawScale.kt
@@ -18,10 +18,9 @@ package com.android.compose.animation.scene.transformation
import androidx.compose.ui.geometry.Offset
import com.android.compose.animation.scene.ContentKey
-import com.android.compose.animation.scene.Element
+import com.android.compose.animation.scene.ElementKey
import com.android.compose.animation.scene.ElementMatcher
import com.android.compose.animation.scene.Scale
-import com.android.compose.animation.scene.SceneTransitionLayoutImpl
import com.android.compose.animation.scene.content.state.TransitionState
/**
@@ -35,11 +34,9 @@ internal class DrawScale(
private val pivot: Offset = Offset.Unspecified,
) : PropertyTransformation<Scale> {
- override fun transform(
- layoutImpl: SceneTransitionLayoutImpl,
+ override fun PropertyTransformationScope.transform(
content: ContentKey,
- element: Element,
- stateInContent: Element.State,
+ element: ElementKey,
transition: TransitionState.Transition,
value: Scale,
): Scale {
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/EdgeTranslate.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/EdgeTranslate.kt
index 031f50e17225..4ae07c541011 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/EdgeTranslate.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/EdgeTranslate.kt
@@ -19,9 +19,8 @@ package com.android.compose.animation.scene.transformation
import androidx.compose.ui.geometry.Offset
import com.android.compose.animation.scene.ContentKey
import com.android.compose.animation.scene.Edge
-import com.android.compose.animation.scene.Element
+import com.android.compose.animation.scene.ElementKey
import com.android.compose.animation.scene.ElementMatcher
-import com.android.compose.animation.scene.SceneTransitionLayoutImpl
import com.android.compose.animation.scene.content.state.TransitionState
/** Translate an element from an edge of the layout. */
@@ -30,21 +29,18 @@ internal class EdgeTranslate(
private val edge: Edge,
private val startsOutsideLayoutBounds: Boolean = true,
) : PropertyTransformation<Offset> {
- override fun transform(
- layoutImpl: SceneTransitionLayoutImpl,
+ override fun PropertyTransformationScope.transform(
content: ContentKey,
- element: Element,
- stateInContent: Element.State,
+ element: ElementKey,
transition: TransitionState.Transition,
value: Offset,
): Offset {
- val sceneSize = layoutImpl.content(content).targetSize
- val elementSize = stateInContent.targetSize
- if (elementSize == Element.SizeUnspecified) {
- return value
- }
+ val sceneSize =
+ content.targetSize()
+ ?: error("Content ${content.debugName} does not have a target size")
+ val elementSize = element.targetSize(content) ?: return value
- return when (edge.resolve(layoutImpl.layoutDirection)) {
+ return when (edge.resolve(layoutDirection)) {
Edge.Resolved.Top ->
if (startsOutsideLayoutBounds) {
Offset(value.x, -elementSize.height.toFloat())
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Fade.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Fade.kt
index 078aa0f7efe9..c11ec977fe2b 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Fade.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Fade.kt
@@ -17,18 +17,15 @@
package com.android.compose.animation.scene.transformation
import com.android.compose.animation.scene.ContentKey
-import com.android.compose.animation.scene.Element
+import com.android.compose.animation.scene.ElementKey
import com.android.compose.animation.scene.ElementMatcher
-import com.android.compose.animation.scene.SceneTransitionLayoutImpl
import com.android.compose.animation.scene.content.state.TransitionState
/** Fade an element in or out. */
internal class Fade(override val matcher: ElementMatcher) : PropertyTransformation<Float> {
- override fun transform(
- layoutImpl: SceneTransitionLayoutImpl,
+ override fun PropertyTransformationScope.transform(
content: ContentKey,
- element: Element,
- stateInContent: Element.State,
+ element: ElementKey,
transition: TransitionState.Transition,
value: Float,
): Float {
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/ScaleSize.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/ScaleSize.kt
index 5f3fdaf67e5f..a159a5b5b2bd 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/ScaleSize.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/ScaleSize.kt
@@ -18,9 +18,8 @@ package com.android.compose.animation.scene.transformation
import androidx.compose.ui.unit.IntSize
import com.android.compose.animation.scene.ContentKey
-import com.android.compose.animation.scene.Element
+import com.android.compose.animation.scene.ElementKey
import com.android.compose.animation.scene.ElementMatcher
-import com.android.compose.animation.scene.SceneTransitionLayoutImpl
import com.android.compose.animation.scene.content.state.TransitionState
import kotlin.math.roundToInt
@@ -33,11 +32,9 @@ internal class ScaleSize(
private val width: Float = 1f,
private val height: Float = 1f,
) : PropertyTransformation<IntSize> {
- override fun transform(
- layoutImpl: SceneTransitionLayoutImpl,
+ override fun PropertyTransformationScope.transform(
content: ContentKey,
- element: Element,
- stateInContent: Element.State,
+ element: ElementKey,
transition: TransitionState.Transition,
value: IntSize,
): IntSize {
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Transformation.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Transformation.kt
index de7f418f219a..d38067d9af38 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Transformation.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Transformation.kt
@@ -18,13 +18,15 @@ package com.android.compose.animation.scene.transformation
import androidx.compose.animation.core.Easing
import androidx.compose.animation.core.LinearEasing
+import androidx.compose.ui.unit.Density
+import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.util.fastCoerceAtLeast
import androidx.compose.ui.util.fastCoerceAtMost
import androidx.compose.ui.util.fastCoerceIn
import com.android.compose.animation.scene.ContentKey
-import com.android.compose.animation.scene.Element
+import com.android.compose.animation.scene.ElementKey
import com.android.compose.animation.scene.ElementMatcher
-import com.android.compose.animation.scene.SceneTransitionLayoutImpl
+import com.android.compose.animation.scene.ElementStateScope
import com.android.compose.animation.scene.content.state.TransitionState
/** A transformation applied to one or more elements during a transition. */
@@ -56,22 +58,30 @@ internal class SharedElementTransformation(
) : Transformation
/** A transformation that changes the value of an element property, like its size or offset. */
-internal sealed interface PropertyTransformation<T> : Transformation {
+interface PropertyTransformation<T> : Transformation {
/**
- * Transform [value], i.e. the value of the transformed property without this transformation.
+ * Return the transformed value for the given property, i.e.:
+ * - the value at progress = 0% for elements that are entering the layout (i.e. elements in the
+ * content we are transitioning to).
+ * - the value at progress = 100% for elements that are leaving the layout (i.e. elements in the
+ * content we are transitioning from).
+ *
+ * The returned value will be interpolated using the [transition] progress and [value], the
+ * value of the property when we are idle.
*/
- // TODO(b/290184746): Figure out a public API for custom transformations that don't have access
- // to these internal classes.
- fun transform(
- layoutImpl: SceneTransitionLayoutImpl,
+ fun PropertyTransformationScope.transform(
content: ContentKey,
- element: Element,
- stateInContent: Element.State,
+ element: ElementKey,
transition: TransitionState.Transition,
value: T,
): T
}
+interface PropertyTransformationScope : Density, ElementStateScope {
+ /** The current [direction][LayoutDirection] of the layout. */
+ val layoutDirection: LayoutDirection
+}
+
/**
* A [PropertyTransformation] associated to a range. This is a helper class so that normal
* implementations of [PropertyTransformation] don't have to take care of reversing their range when
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 70142717fffe..af0a6edfa2fb 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
@@ -21,10 +21,9 @@ import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import com.android.compose.animation.scene.ContentKey
-import com.android.compose.animation.scene.Element
+import com.android.compose.animation.scene.ElementKey
import com.android.compose.animation.scene.ElementMatcher
import com.android.compose.animation.scene.OverscrollScope
-import com.android.compose.animation.scene.SceneTransitionLayoutImpl
import com.android.compose.animation.scene.content.state.TransitionState
internal class Translate(
@@ -32,15 +31,13 @@ internal class Translate(
private val x: Dp = 0.dp,
private val y: Dp = 0.dp,
) : PropertyTransformation<Offset> {
- override fun transform(
- layoutImpl: SceneTransitionLayoutImpl,
+ override fun PropertyTransformationScope.transform(
content: ContentKey,
- element: Element,
- stateInContent: Element.State,
+ element: ElementKey,
transition: TransitionState.Transition,
value: Offset,
): Offset {
- return with(layoutImpl.density) { Offset(value.x + x.toPx(), value.y + y.toPx()) }
+ return Offset(value.x + x.toPx(), value.y + y.toPx())
}
}
@@ -51,11 +48,9 @@ internal class OverscrollTranslate(
) : PropertyTransformation<Offset> {
private val cachedOverscrollScope = CachedOverscrollScope()
- override fun transform(
- layoutImpl: SceneTransitionLayoutImpl,
+ override fun PropertyTransformationScope.transform(
content: ContentKey,
- element: Element,
- stateInContent: Element.State,
+ element: ElementKey,
transition: TransitionState.Transition,
value: Offset,
): Offset {
@@ -64,7 +59,7 @@ internal class OverscrollTranslate(
// that this method was invoked after performing this check.
val overscrollProperties = transition as TransitionState.HasOverscrollProperties
val overscrollScope =
- cachedOverscrollScope.getFromCacheOrCompute(layoutImpl.density, overscrollProperties)
+ cachedOverscrollScope.getFromCacheOrCompute(density = this, overscrollProperties)
return Offset(x = value.x + overscrollScope.x(), y = value.y + overscrollScope.y())
}
@@ -75,7 +70,7 @@ internal class OverscrollTranslate(
* [TransitionState.HasOverscrollProperties]. This helps avoid recreating a scope every frame
* whenever an overscroll transition is computed.
*/
-private class CachedOverscrollScope() {
+private class CachedOverscrollScope {
private var previousScope: OverscrollScope? = null
private var previousDensity: Density? = null
private var previousOverscrollProperties: TransitionState.HasOverscrollProperties? = null
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transition/Seek.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transition/Seek.kt
index 715d979e116e..2b33224022fc 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transition/Seek.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transition/Seek.kt
@@ -30,6 +30,8 @@ import com.android.compose.animation.scene.TransitionKey
import com.android.compose.animation.scene.UserActionResult
import com.android.compose.animation.scene.createSwipeAnimation
import kotlin.coroutines.cancellation.CancellationException
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Job
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.collectLatest
@@ -141,6 +143,7 @@ internal suspend fun <T : ContentKey> animateProgress(
progress: Flow<Float>,
commitSpec: AnimationSpec<Float>?,
cancelSpec: AnimationSpec<Float>?,
+ animationScope: CoroutineScope? = null,
) {
fun animateOffset(targetContent: T, spec: AnimationSpec<Float>?) {
if (state.transitionState != animation.contentTransition || animation.isAnimatingOffset()) {
@@ -176,12 +179,20 @@ internal suspend fun <T : ContentKey> animateProgress(
}
// Start the transition.
- state.startTransition(animation.contentTransition)
+ animationScope?.launch { startTransition(state, animation, collectionJob) }
+ ?: startTransition(state, animation, collectionJob)
+ }
+}
- // The transition is done. Cancel the collection in case the transition was finished because
- // it was interrupted by another transition.
- if (collectionJob.isActive) {
- collectionJob.cancel()
- }
+private suspend fun <T : ContentKey> startTransition(
+ state: MutableSceneTransitionLayoutStateImpl,
+ animation: SwipeAnimation<T>,
+ progressCollectionJob: Job,
+) {
+ state.startTransition(animation.contentTransition)
+ // The transition is done. Cancel the collection in case the transition was finished
+ // because it was interrupted by another transition.
+ if (progressCollectionJob.isActive) {
+ progressCollectionJob.cancel()
}
}
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 55fd3440ea07..c803097134de 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
@@ -28,7 +28,6 @@ import android.graphics.Point
import android.graphics.Rect
import android.graphics.drawable.BitmapDrawable
import android.hardware.biometrics.BiometricFingerprintConstants
-import android.hardware.biometrics.Flags.FLAG_CUSTOM_BIOMETRIC_PROMPT
import android.hardware.biometrics.PromptContentItemBulletedText
import android.hardware.biometrics.PromptContentView
import android.hardware.biometrics.PromptContentViewWithMoreOptionsButton
@@ -163,7 +162,7 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
naturalDisplayWidth = 1000,
naturalDisplayHeight = 3000,
scaleFactor = 1f,
- rotation = if (isLandscape) Surface.ROTATION_90 else Surface.ROTATION_0
+ rotation = if (isLandscape) Surface.ROTATION_90 else Surface.ROTATION_0,
)
private lateinit var promptContentView: PromptContentView
@@ -180,21 +179,21 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
whenever(
kosmos.packageManager.getApplicationInfo(
eq(OP_PACKAGE_NAME_WITH_APP_LOGO),
- anyInt()
+ anyInt(),
)
)
.thenReturn(applicationInfoWithIconAndDescription)
whenever(
kosmos.packageManager.getApplicationInfo(
eq(OP_PACKAGE_NAME_WITH_ACTIVITY_LOGO),
- anyInt()
+ anyInt(),
)
)
.thenReturn(applicationInfoWithIconAndDescription)
whenever(
kosmos.packageManager.getApplicationInfo(
eq(OP_PACKAGE_NAME_CAN_NOT_BE_FOUND),
- anyInt()
+ anyInt(),
)
)
.thenThrow(NameNotFoundException())
@@ -220,13 +219,13 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
overrideResource(logoResFromApp, logoDrawableFromAppRes)
overrideResource(
R.array.config_useActivityLogoForBiometricPrompt,
- arrayOf(OP_PACKAGE_NAME_WITH_ACTIVITY_LOGO)
+ arrayOf(OP_PACKAGE_NAME_WITH_ACTIVITY_LOGO),
)
overrideResource(R.dimen.biometric_dialog_fingerprint_icon_width, mockFingerprintIconWidth)
overrideResource(
R.dimen.biometric_dialog_fingerprint_icon_height,
- mockFingerprintIconHeight
+ mockFingerprintIconHeight,
)
overrideResource(R.dimen.biometric_dialog_face_icon_size, mockFaceIconSize)
@@ -243,7 +242,7 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
it.sensorType.toSensorType(),
it.allLocations.associateBy { sensorLocationInternal ->
sensorLocationInternal.displayId
- }
+ },
)
}
@@ -441,7 +440,7 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
kosmos.promptViewModel.showAuthenticated(
modality = testCase.authenticatedModality,
- dismissAfterDelay = DELAY
+ dismissAfterDelay = DELAY,
)
// SFPS test cases
@@ -513,7 +512,7 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
kosmos.promptViewModel.showAuthenticated(
modality = testCase.authenticatedModality,
dismissAfterDelay = DELAY,
- "TEST"
+ "TEST",
)
if (testCase.isFingerprintOnly) {
@@ -558,7 +557,7 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
kosmos.promptViewModel.showAuthenticated(
modality = testCase.authenticatedModality,
- dismissAfterDelay = DELAY
+ dismissAfterDelay = DELAY,
)
if (testCase.isFaceOnly) {
@@ -598,7 +597,7 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
kosmos.promptViewModel.showAuthenticated(
modality = testCase.authenticatedModality,
- dismissAfterDelay = DELAY
+ dismissAfterDelay = DELAY,
)
kosmos.promptViewModel.confirmAuthenticated()
@@ -701,7 +700,7 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
.isEqualTo(
Pair(
expectedUdfpsOverlayParams.sensorBounds.width(),
- expectedUdfpsOverlayParams.sensorBounds.height()
+ expectedUdfpsOverlayParams.sensorBounds.height(),
)
)
} else {
@@ -834,7 +833,7 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
kosmos.promptViewModel.showAuthenticated(
modality = testCase.authenticatedModality,
- dismissAfterDelay = DELAY
+ dismissAfterDelay = DELAY,
)
kosmos.displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_0)
@@ -907,10 +906,7 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
}
)
- assertButtonsVisible(
- cancel = expectConfirmation,
- confirm = expectConfirmation,
- )
+ assertButtonsVisible(cancel = expectConfirmation, confirm = expectConfirmation)
}
@Test
@@ -1158,10 +1154,7 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
testScheduler.runCurrent()
assertThat(messages)
- .containsExactly(
- PromptMessage.Empty,
- PromptMessage.Error(expectedErrorMessage),
- )
+ .containsExactly(PromptMessage.Empty, PromptMessage.Error(expectedErrorMessage))
.inOrder()
testScheduler.advanceUntilIdle()
@@ -1221,10 +1214,7 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
assertThat(authenticated?.needsUserConfirmation).isEqualTo(expectConfirmation)
if (expectConfirmation) {
assertThat(size).isEqualTo(PromptSize.MEDIUM)
- assertButtonsVisible(
- cancel = true,
- confirm = true,
- )
+ assertButtonsVisible(cancel = true, confirm = true)
kosmos.promptViewModel.confirmAuthenticated()
assertThat(message).isEqualTo(PromptMessage.Empty)
@@ -1251,10 +1241,7 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
assertThat(authenticated?.needsUserConfirmation).isEqualTo(expectConfirmation)
if (expectConfirmation) {
assertThat(size).isEqualTo(PromptSize.MEDIUM)
- assertButtonsVisible(
- cancel = true,
- confirm = true,
- )
+ assertButtonsVisible(cancel = true, confirm = true)
if (testCase.modalities.hasSfps) {
kosmos.promptViewModel.showAuthenticated(BiometricModality.Fingerprint, 0)
@@ -1290,10 +1277,7 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
if (expectConfirmation) {
if (testCase.isFaceOnly) {
assertThat(size).isEqualTo(PromptSize.MEDIUM)
- assertButtonsVisible(
- cancel = true,
- confirm = true,
- )
+ assertButtonsVisible(cancel = true, confirm = true)
kosmos.promptViewModel.confirmAuthenticated()
} else if (testCase.isCoex) {
@@ -1323,10 +1307,7 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
assertThat(authenticated?.needsUserConfirmation).isEqualTo(expectConfirmation)
if (expectConfirmation) {
assertThat(size).isEqualTo(PromptSize.MEDIUM)
- assertButtonsVisible(
- cancel = true,
- confirm = true,
- )
+ assertButtonsVisible(cancel = true, confirm = true)
kosmos.promptViewModel.confirmAuthenticated()
assertThat(message).isEqualTo(PromptMessage.Empty)
@@ -1398,10 +1379,7 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
assertThat(authenticating).isFalse()
assertThat(authenticated?.isAuthenticated).isTrue()
assertThat(authenticated?.needsUserConfirmation).isEqualTo(expectConfirmation)
- assertButtonsVisible(
- cancel = expectConfirmation,
- confirm = expectConfirmation,
- )
+ assertButtonsVisible(cancel = expectConfirmation, confirm = expectConfirmation)
}
@Test
@@ -1421,7 +1399,7 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
errorMessage,
messageAfterError = helpMessage,
authenticateAfterError = false,
- failedModality = testCase.authenticatedModality
+ failedModality = testCase.authenticatedModality,
)
}
@@ -1472,7 +1450,7 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
kosmos.promptViewModel.onAnnounceAccessibilityHint(
obtainMotionEvent(MotionEvent.ACTION_HOVER_ENTER),
- true
+ true,
)
if (testCase.modalities.hasUdfps) {
@@ -1497,14 +1475,13 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
kosmos.promptViewModel.onAnnounceAccessibilityHint(
obtainMotionEvent(MotionEvent.ACTION_HOVER_ENTER),
- true
+ true,
)
assertThat(hint.isNullOrBlank()).isTrue()
}
@Test
- @EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT)
fun descriptionOverriddenByVerticalListContentView() =
runGenericTest(description = "test description", contentView = promptContentView) {
val contentView by collectLastValue(kosmos.promptViewModel.contentView)
@@ -1515,11 +1492,10 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
}
@Test
- @EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT)
fun descriptionOverriddenByContentViewWithMoreOptionsButton() =
runGenericTest(
description = "test description",
- contentView = promptContentViewWithMoreOptionsButton
+ contentView = promptContentViewWithMoreOptionsButton,
) {
val contentView by collectLastValue(kosmos.promptViewModel.contentView)
val description by collectLastValue(kosmos.promptViewModel.description)
@@ -1529,7 +1505,6 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
}
@Test
- @EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT)
fun descriptionWithoutContentView() =
runGenericTest(description = "test description") {
val contentView by collectLastValue(kosmos.promptViewModel.contentView)
@@ -1540,7 +1515,6 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
}
@Test
- @EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT)
fun logo_nullIfPkgNameNotFound() =
runGenericTest(packageName = OP_PACKAGE_NAME_CAN_NOT_BE_FOUND) {
val logoInfo by collectLastValue(kosmos.promptViewModel.logoInfo)
@@ -1549,7 +1523,6 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
}
@Test
- @EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT)
fun logo_defaultFromActivityInfo() =
runGenericTest(packageName = OP_PACKAGE_NAME_WITH_ACTIVITY_LOGO) {
val logoInfo by collectLastValue(kosmos.promptViewModel.logoInfo)
@@ -1564,7 +1537,6 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
}
@Test
- @EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT)
fun logo_defaultIsNull() =
runGenericTest(packageName = OP_PACKAGE_NAME_NO_ICON) {
val logoInfo by collectLastValue(kosmos.promptViewModel.logoInfo)
@@ -1573,7 +1545,6 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
}
@Test
- @EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT)
fun logo_default() = runGenericTest {
val logoInfo by collectLastValue(kosmos.promptViewModel.logoInfo)
assertThat(logoInfo).isNotNull()
@@ -1581,7 +1552,6 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
}
@Test
- @EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT)
fun logo_resSetByApp() =
runGenericTest(logoRes = logoResFromApp) {
val expectedBitmap = context.getDrawable(logoResFromApp).toBitmap()
@@ -1591,7 +1561,6 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
}
@Test
- @EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT)
fun logo_bitmapSetByApp() =
runGenericTest(logoBitmap = logoBitmapFromApp) {
val logoInfo by collectLastValue(kosmos.promptViewModel.logoInfo)
@@ -1599,7 +1568,6 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
}
@Test
- @EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT)
fun logoDescription_emptyIfPkgNameNotFound() =
runGenericTest(packageName = OP_PACKAGE_NAME_CAN_NOT_BE_FOUND) {
val logoInfo by collectLastValue(kosmos.promptViewModel.logoInfo)
@@ -1607,7 +1575,6 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
}
@Test
- @EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT)
fun logoDescription_defaultFromActivityInfo() =
runGenericTest(packageName = OP_PACKAGE_NAME_WITH_ACTIVITY_LOGO) {
val logoInfo by collectLastValue(kosmos.promptViewModel.logoInfo)
@@ -1619,7 +1586,6 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
}
@Test
- @EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT)
fun logoDescription_defaultIsEmpty() =
runGenericTest(packageName = OP_PACKAGE_NAME_NO_ICON) {
val logoInfo by collectLastValue(kosmos.promptViewModel.logoInfo)
@@ -1627,14 +1593,12 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
}
@Test
- @EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT)
fun logoDescription_default() = runGenericTest {
val logoInfo by collectLastValue(kosmos.promptViewModel.logoInfo)
assertThat(logoInfo!!.second).isEqualTo(defaultLogoDescriptionFromAppInfo)
}
@Test
- @EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT)
fun logoDescription_setByApp() =
runGenericTest(logoDescription = logoDescriptionFromApp) {
val logoInfo by collectLastValue(kosmos.promptViewModel.logoInfo)
@@ -1826,7 +1790,7 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
kosmos.biometricStatusRepository.setFingerprintAcquiredStatus(
AcquiredFingerprintAuthenticationStatus(
AuthenticationReason.BiometricPromptAuthentication,
- BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_UNKNOWN
+ BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_UNKNOWN,
)
)
@@ -1893,7 +1857,7 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
fingerprint =
fingerprintSensorPropertiesInternal(
strong = true,
- sensorType = FingerprintSensorProperties.TYPE_POWER_BUTTON
+ sensorType = FingerprintSensorProperties.TYPE_POWER_BUTTON,
)
.first(),
authenticatedModality = BiometricModality.Fingerprint,
@@ -1903,7 +1867,7 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
fingerprint =
fingerprintSensorPropertiesInternal(
strong = true,
- sensorType = FingerprintSensorProperties.TYPE_POWER_BUTTON
+ sensorType = FingerprintSensorProperties.TYPE_POWER_BUTTON,
)
.first(),
authenticatedModality = BiometricModality.Fingerprint,
@@ -1913,7 +1877,7 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
fingerprint =
fingerprintSensorPropertiesInternal(
strong = true,
- sensorType = FingerprintSensorProperties.TYPE_UDFPS_OPTICAL
+ sensorType = FingerprintSensorProperties.TYPE_UDFPS_OPTICAL,
)
.first(),
authenticatedModality = BiometricModality.Fingerprint,
@@ -1932,7 +1896,7 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
fingerprint =
fingerprintSensorPropertiesInternal(
strong = true,
- sensorType = FingerprintSensorProperties.TYPE_POWER_BUTTON
+ sensorType = FingerprintSensorProperties.TYPE_POWER_BUTTON,
)
.first(),
authenticatedModality = BiometricModality.Fingerprint,
@@ -1958,7 +1922,7 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
fingerprint =
fingerprintSensorPropertiesInternal(
strong = true,
- sensorType = FingerprintSensorProperties.TYPE_POWER_BUTTON
+ sensorType = FingerprintSensorProperties.TYPE_POWER_BUTTON,
)
.first(),
authenticatedModality = BiometricModality.Fingerprint,
@@ -1969,7 +1933,7 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
fingerprint =
fingerprintSensorPropertiesInternal(
strong = true,
- sensorType = FingerprintSensorProperties.TYPE_UDFPS_OPTICAL
+ sensorType = FingerprintSensorProperties.TYPE_UDFPS_OPTICAL,
)
.first(),
authenticatedModality = BiometricModality.Fingerprint,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryLocalImplTest.kt
index 8ae9d2e8f7e8..55d7d08e8519 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryLocalImplTest.kt
@@ -73,7 +73,7 @@ import org.mockito.kotlin.whenever
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWith(AndroidJUnit4::class)
-class CommunalWidgetRepositoryImplTest : SysuiTestCase() {
+class CommunalWidgetRepositoryLocalImplTest : SysuiTestCase() {
@Mock private lateinit var appWidgetHost: CommunalAppWidgetHost
@Mock private lateinit var providerInfoA: AppWidgetProviderInfo
@Mock private lateinit var providerInfoB: AppWidgetProviderInfo
@@ -105,14 +105,14 @@ class CommunalWidgetRepositoryImplTest : SysuiTestCase() {
"com.android.fake/WidgetProviderC",
)
- private lateinit var underTest: CommunalWidgetRepositoryImpl
+ private lateinit var underTest: CommunalWidgetRepositoryLocalImpl
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
fakeWidgets = MutableStateFlow(emptyMap())
fakeProviders = MutableStateFlow(emptyMap())
- logBuffer = logcatLogBuffer(name = "CommunalWidgetRepoImplTest")
+ logBuffer = logcatLogBuffer(name = "CommunalWidgetRepoLocalImplTest")
backupUtils = CommunalBackupUtils(kosmos.applicationContext)
setAppWidgetIds(emptyList())
@@ -126,7 +126,7 @@ class CommunalWidgetRepositoryImplTest : SysuiTestCase() {
restoreUser(mainUser)
underTest =
- CommunalWidgetRepositoryImpl(
+ CommunalWidgetRepositoryLocalImpl(
appWidgetHost,
testScope.backgroundScope,
kosmos.testDispatcher,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/shared/model/CommunalWidgetContentModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/shared/model/CommunalWidgetContentModelTest.kt
new file mode 100644
index 000000000000..c21015402f01
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/shared/model/CommunalWidgetContentModelTest.kt
@@ -0,0 +1,81 @@
+/*
+ * 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.systemui.communal.shared.model
+
+import android.appwidget.AppWidgetProviderInfo
+import android.content.ComponentName
+import android.os.Parcel
+import android.os.UserHandle
+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 org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class CommunalWidgetContentModelTest : SysuiTestCase() {
+ @Test
+ fun testParcelizeAvailableWidget() {
+ val widgetToParcelize =
+ CommunalWidgetContentModel.Available(
+ appWidgetId = 1,
+ providerInfo =
+ AppWidgetProviderInfo().apply { provider = ComponentName("pkg", "cls") },
+ rank = 2,
+ spanY = 3,
+ )
+
+ val parcel = Parcel.obtain()
+ widgetToParcelize.writeToParcel(parcel, flags = 0)
+
+ parcel.setDataPosition(0)
+
+ // Only checking fields are equal and not complete equality because not all fields are
+ // specified in the fake AppWidgetProviderInfo
+ val widgetFromParcel =
+ CommunalWidgetContentModel.createFromParcel(parcel)
+ as CommunalWidgetContentModel.Available
+ assertThat(widgetFromParcel.appWidgetId).isEqualTo(widgetToParcelize.appWidgetId)
+ assertThat(widgetFromParcel.rank).isEqualTo(widgetToParcelize.rank)
+ assertThat(widgetFromParcel.spanY).isEqualTo(widgetToParcelize.spanY)
+ assertThat(widgetFromParcel.providerInfo.provider)
+ .isEqualTo(widgetToParcelize.providerInfo.provider)
+ }
+
+ @Test
+ fun testParcelizePendingWidget() {
+ val widgetToParcelize =
+ CommunalWidgetContentModel.Pending(
+ appWidgetId = 2,
+ rank = 3,
+ componentName = ComponentName("pkg", "cls"),
+ icon = null,
+ user = UserHandle(0),
+ spanY = 6,
+ )
+
+ val parcel = Parcel.obtain()
+ widgetToParcelize.writeToParcel(parcel, flags = 0)
+
+ parcel.setDataPosition(0)
+
+ val widgetFromParcel = CommunalWidgetContentModel.createFromParcel(parcel)
+ assertThat(widgetFromParcel).isEqualTo(widgetToParcelize)
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/widgets/CommunalAppWidgetHostTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/widgets/CommunalAppWidgetHostTest.kt
index 1e79112eefe3..18513fc496b4 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/widgets/CommunalAppWidgetHostTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/widgets/CommunalAppWidgetHostTest.kt
@@ -21,6 +21,7 @@ import android.testing.TestableLooper.RunWithLooper
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.communal.shared.model.fakeGlanceableHubMultiUserHelper
import com.android.systemui.communal.widgets.CommunalAppWidgetHost
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.kosmos.applicationCoroutineScope
@@ -61,6 +62,7 @@ class CommunalAppWidgetHostTest : SysuiTestCase() {
backgroundScope = kosmos.applicationCoroutineScope,
hostId = 116,
logBuffer = logcatLogBuffer("CommunalAppWidgetHostTest"),
+ glanceableHubMultiUserHelper = kosmos.fakeGlanceableHubMultiUserHelper,
)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostStartableTest.kt
index c9f3f1453492..9ef2b190fdd7 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostStartableTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostStartableTest.kt
@@ -24,10 +24,14 @@ import com.android.systemui.Flags.FLAG_COMMUNAL_HUB
import com.android.systemui.SysuiTestCase
import com.android.systemui.communal.data.repository.fakeCommunalWidgetRepository
import com.android.systemui.communal.domain.interactor.communalInteractor
+import com.android.systemui.communal.domain.interactor.communalSettingsInteractor
+import com.android.systemui.communal.shared.model.FakeGlanceableHubMultiUserHelper
+import com.android.systemui.communal.shared.model.fakeGlanceableHubMultiUserHelper
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.flags.Flags
import com.android.systemui.flags.fakeFeatureFlagsClassic
import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
+import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.kosmos.testDispatcher
import com.android.systemui.kosmos.testScope
@@ -58,6 +62,9 @@ class CommunalAppWidgetHostStartableTest : SysuiTestCase() {
@Mock private lateinit var appWidgetHost: CommunalAppWidgetHost
@Mock private lateinit var communalWidgetHost: CommunalWidgetHost
+ private lateinit var widgetManager: GlanceableHubWidgetManager
+ private lateinit var helper: FakeGlanceableHubMultiUserHelper
+
private lateinit var appWidgetIdToRemove: MutableSharedFlow<Int>
private lateinit var underTest: CommunalAppWidgetHostStartable
@@ -69,17 +76,23 @@ class CommunalAppWidgetHostStartableTest : SysuiTestCase() {
kosmos.fakeFeatureFlagsClassic.set(Flags.COMMUNAL_SERVICE_ENABLED, true)
mSetFlagsRule.enableFlags(FLAG_COMMUNAL_HUB)
+ widgetManager = kosmos.mockGlanceableHubWidgetManager
+ helper = kosmos.fakeGlanceableHubMultiUserHelper
appWidgetIdToRemove = MutableSharedFlow()
whenever(appWidgetHost.appWidgetIdToRemove).thenReturn(appWidgetIdToRemove)
underTest =
CommunalAppWidgetHostStartable(
- appWidgetHost,
- communalWidgetHost,
- kosmos.communalInteractor,
- kosmos.fakeUserTracker,
+ { appWidgetHost },
+ { communalWidgetHost },
+ { kosmos.communalInteractor },
+ { kosmos.communalSettingsInteractor },
+ { kosmos.keyguardInteractor },
+ { kosmos.fakeUserTracker },
kosmos.applicationCoroutineScope,
kosmos.testDispatcher,
+ { widgetManager },
+ helper,
)
}
@@ -211,7 +224,7 @@ class CommunalAppWidgetHostStartableTest : SysuiTestCase() {
fakeCommunalWidgetRepository.addWidget(appWidgetId = 1, userId = USER_INFO_WORK.id)
fakeCommunalWidgetRepository.addPendingWidget(
appWidgetId = 2,
- userId = USER_INFO_WORK.id
+ userId = USER_INFO_WORK.id,
)
fakeCommunalWidgetRepository.addWidget(appWidgetId = 3, userId = MAIN_USER_INFO.id)
@@ -246,16 +259,42 @@ class CommunalAppWidgetHostStartableTest : SysuiTestCase() {
}
}
- private suspend fun setCommunalAvailable(available: Boolean) =
+ @Test
+ fun onStartHeadlessSystemUser_registerWidgetManager_whenCommunalIsAvailable() =
+ with(kosmos) {
+ testScope.runTest {
+ helper.setIsInHeadlessSystemUser(true)
+ underTest.start()
+ runCurrent()
+ verify(widgetManager, never()).register()
+ verify(widgetManager, never()).unregister()
+
+ // Binding to the service does not require keyguard showing
+ setCommunalAvailable(true, setKeyguardShowing = false)
+ runCurrent()
+ verify(widgetManager).register()
+
+ setCommunalAvailable(false)
+ runCurrent()
+ verify(widgetManager).unregister()
+ }
+ }
+
+ private suspend fun setCommunalAvailable(
+ available: Boolean,
+ setKeyguardShowing: Boolean = true,
+ ) =
with(kosmos) {
fakeKeyguardRepository.setIsEncryptedOrLockdown(false)
fakeUserRepository.setSelectedUserInfo(MAIN_USER_INFO)
- fakeKeyguardRepository.setKeyguardShowing(true)
+ if (setKeyguardShowing) {
+ fakeKeyguardRepository.setKeyguardShowing(true)
+ }
val settingsValue = if (available) 1 else 0
fakeSettings.putIntForUser(
Settings.Secure.GLANCEABLE_HUB_ENABLED,
settingsValue,
- MAIN_USER_INFO.id
+ MAIN_USER_INFO.id,
)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/CommunalWidgetHostTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/CommunalWidgetHostTest.kt
index 054e516db943..017c77828cdc 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/CommunalWidgetHostTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/CommunalWidgetHostTest.kt
@@ -26,6 +26,7 @@ import android.os.UserHandle
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.communal.shared.model.fakeGlanceableHubMultiUserHelper
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.coroutines.collectValues
import com.android.systemui.kosmos.applicationCoroutineScope
@@ -84,7 +85,7 @@ class CommunalWidgetHostTest : SysuiTestCase() {
any<Int>(),
any<UserHandle>(),
any<ComponentName>(),
- any<Bundle>()
+ any<Bundle>(),
)
)
.thenReturn(true)
@@ -96,6 +97,7 @@ class CommunalWidgetHostTest : SysuiTestCase() {
appWidgetHost,
selectedUserInteractor,
logcatLogBuffer("CommunalWidgetHostTest"),
+ glanceableHubMultiUserHelper = kosmos.fakeGlanceableHubMultiUserHelper,
)
}
@@ -162,7 +164,7 @@ class CommunalWidgetHostTest : SysuiTestCase() {
any<Int>(),
any<UserHandle>(),
any<ComponentName>(),
- any<Bundle>()
+ any<Bundle>(),
)
)
.thenReturn(false)
@@ -283,12 +285,7 @@ class CommunalWidgetHostTest : SysuiTestCase() {
// all providers are emitted at once
assertThat(providerInfoValues).hasSize(2)
assertThat(providerInfoValues[1])
- .containsExactlyEntriesIn(
- mapOf(
- Pair(1, providerInfo1),
- Pair(2, providerInfo2),
- )
- )
+ .containsExactlyEntriesIn(mapOf(Pair(1, providerInfo1), Pair(2, providerInfo2)))
}
@Test
@@ -304,12 +301,7 @@ class CommunalWidgetHostTest : SysuiTestCase() {
// Assert that the provider info map is populated
val providerInfo by collectLastValue(underTest.appWidgetProviders)
assertThat(providerInfo)
- .containsExactlyEntriesIn(
- mapOf(
- Pair(1, providerInfo1),
- Pair(2, providerInfo2),
- )
- )
+ .containsExactlyEntriesIn(mapOf(Pair(1, providerInfo1), Pair(2, providerInfo2)))
// Host stop listening
observer.onHostStopListening()
@@ -332,12 +324,7 @@ class CommunalWidgetHostTest : SysuiTestCase() {
// Assert that the provider info map is populated
assertThat(providerInfo)
- .containsExactlyEntriesIn(
- mapOf(
- Pair(1, providerInfo1),
- Pair(2, providerInfo2),
- )
- )
+ .containsExactlyEntriesIn(mapOf(Pair(1, providerInfo1), Pair(2, providerInfo2)))
// Provider info for widget 1 updated
val listener =
@@ -349,12 +336,7 @@ class CommunalWidgetHostTest : SysuiTestCase() {
// Assert that the update is reflected in the flow
assertThat(providerInfo)
- .containsExactlyEntriesIn(
- mapOf(
- Pair(1, providerInfo3),
- Pair(2, providerInfo2),
- )
- )
+ .containsExactlyEntriesIn(mapOf(Pair(1, providerInfo3), Pair(2, providerInfo2)))
}
@Test
@@ -371,12 +353,7 @@ class CommunalWidgetHostTest : SysuiTestCase() {
// Assert that the provider info map is populated
assertThat(providerInfo)
- .containsExactlyEntriesIn(
- mapOf(
- Pair(1, providerInfo1),
- Pair(2, providerInfo2),
- )
- )
+ .containsExactlyEntriesIn(mapOf(Pair(1, providerInfo1), Pair(2, providerInfo2)))
// Bind a new widget
whenever(appWidgetHost.allocateAppWidgetId()).thenReturn(3)
@@ -388,11 +365,7 @@ class CommunalWidgetHostTest : SysuiTestCase() {
// Assert that the new provider is reflected in the flow
assertThat(providerInfo)
.containsExactlyEntriesIn(
- mapOf(
- Pair(1, providerInfo1),
- Pair(2, providerInfo2),
- Pair(3, providerInfo3),
- )
+ mapOf(Pair(1, providerInfo1), Pair(2, providerInfo2), Pair(3, providerInfo3))
)
}
@@ -410,31 +383,21 @@ class CommunalWidgetHostTest : SysuiTestCase() {
// Assert that the provider info map is populated
assertThat(providerInfo)
- .containsExactlyEntriesIn(
- mapOf(
- Pair(1, providerInfo1),
- Pair(2, providerInfo2),
- )
- )
+ .containsExactlyEntriesIn(mapOf(Pair(1, providerInfo1), Pair(2, providerInfo2)))
// Remove widget 1
observer.onDeleteAppWidgetId(1)
runCurrent()
// Assert that provider info for widget 1 is removed
- assertThat(providerInfo)
- .containsExactlyEntriesIn(
- mapOf(
- Pair(2, providerInfo2),
- )
- )
+ assertThat(providerInfo).containsExactlyEntriesIn(mapOf(Pair(2, providerInfo2)))
}
private fun selectUser() {
kosmos.fakeUserRepository.selectedUser.value =
SelectedUserModel(
userInfo = UserInfo(0, "Current user", 0),
- selectionStatus = SelectionStatus.SELECTION_COMPLETE
+ selectionStatus = SelectionStatus.SELECTION_COMPLETE,
)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/GlanceableHubWidgetManagerServiceTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/GlanceableHubWidgetManagerServiceTest.kt
new file mode 100644
index 000000000000..44ce08514dee
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/GlanceableHubWidgetManagerServiceTest.kt
@@ -0,0 +1,319 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.widgets
+
+import android.appwidget.AppWidgetHost
+import android.appwidget.AppWidgetProviderInfo
+import android.content.ComponentName
+import android.content.Intent
+import android.os.UserHandle
+import android.testing.TestableLooper
+import android.widget.RemoteViews
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.communal.data.repository.fakeCommunalWidgetRepository
+import com.android.systemui.communal.shared.model.CommunalWidgetContentModel
+import com.android.systemui.communal.shared.model.fakeGlanceableHubMultiUserHelper
+import com.android.systemui.communal.widgets.IGlanceableHubWidgetManagerService.IGlanceableHubWidgetsListener
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.log.logcatLogBuffer
+import com.android.systemui.testKosmos
+import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.argumentCaptor
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.never
+import org.mockito.kotlin.verify
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+@RunWith(AndroidJUnit4::class)
+class GlanceableHubWidgetManagerServiceTest : SysuiTestCase() {
+ private val kosmos = testKosmos()
+ private val testScope = kosmos.testScope
+
+ private val appWidgetHostListenerCaptor = argumentCaptor<AppWidgetHost.AppWidgetHostListener>()
+
+ private val widgetRepository = kosmos.fakeCommunalWidgetRepository
+ private val appWidgetHost = mock<CommunalAppWidgetHost>()
+ private val communalWidgetHost = mock<CommunalWidgetHost>()
+ private val multiUserHelper = kosmos.fakeGlanceableHubMultiUserHelper
+
+ private lateinit var underTest: GlanceableHubWidgetManagerService
+
+ @Before
+ fun setup() {
+ underTest =
+ GlanceableHubWidgetManagerService(
+ widgetRepository,
+ appWidgetHost,
+ communalWidgetHost,
+ multiUserHelper,
+ logcatLogBuffer("GlanceableHubWidgetManagerServiceTest"),
+ )
+ }
+
+ @Test
+ fun appWidgetHost_listenWhenServiceIsBound() {
+ underTest.onCreate()
+ verify(appWidgetHost).startListening()
+ verify(communalWidgetHost).startObservingHost()
+ verify(appWidgetHost, never()).stopListening()
+ verify(communalWidgetHost, never()).stopObservingHost()
+
+ underTest.onDestroy()
+ verify(appWidgetHost).stopListening()
+ verify(communalWidgetHost).stopObservingHost()
+ }
+
+ @Test
+ fun widgetsListener_getWidgetUpdates() =
+ testScope.runTest {
+ setupWidgets()
+
+ // Bind service
+ val binder = underTest.onBind(Intent())
+ val service = IGlanceableHubWidgetManagerService.Stub.asInterface(binder)
+
+ // Verify the update is as expected
+ val widgets by collectLastValue(service.listenForWidgetUpdates())
+ assertThat(widgets).hasSize(3)
+ assertThat(widgets?.get(0)?.has(1, "pkg_1/cls_1", 0, 3)).isTrue()
+ assertThat(widgets?.get(1)?.has(2, "pkg_2/cls_2", 1, 3)).isTrue()
+ assertThat(widgets?.get(2)?.has(3, "pkg_3/cls_3", 2, 6)).isTrue()
+ }
+
+ @Test
+ fun widgetsListener_multipleListeners_eachGetsWidgetUpdates() =
+ testScope.runTest {
+ setupWidgets()
+
+ // Bind service
+ val binder = underTest.onBind(Intent())
+ val service = IGlanceableHubWidgetManagerService.Stub.asInterface(binder)
+
+ // Verify the update for the first listener is as expected
+ val widgets1 by collectLastValue(service.listenForWidgetUpdates())
+ assertThat(widgets1).hasSize(3)
+ assertThat(widgets1?.get(0)?.has(1, "pkg_1/cls_1", 0, 3)).isTrue()
+ assertThat(widgets1?.get(1)?.has(2, "pkg_2/cls_2", 1, 3)).isTrue()
+ assertThat(widgets1?.get(2)?.has(3, "pkg_3/cls_3", 2, 6)).isTrue()
+
+ // Verify the update for the second listener is as expected
+ val widgets2 by collectLastValue(service.listenForWidgetUpdates())
+ assertThat(widgets2).hasSize(3)
+ assertThat(widgets2?.get(0)?.has(1, "pkg_1/cls_1", 0, 3)).isTrue()
+ assertThat(widgets2?.get(1)?.has(2, "pkg_2/cls_2", 1, 3)).isTrue()
+ assertThat(widgets2?.get(2)?.has(3, "pkg_3/cls_3", 2, 6)).isTrue()
+ }
+
+ @Test
+ fun setAppWidgetHostListener_getUpdates() =
+ testScope.runTest {
+ // Bind service
+ val binder = underTest.onBind(Intent())
+ val service = IGlanceableHubWidgetManagerService.Stub.asInterface(binder)
+
+ // Set listener
+ val listener = mock<IGlanceableHubWidgetManagerService.IAppWidgetHostListener>()
+ service.setAppWidgetHostListener(1, listener)
+
+ // Verify a listener is set on the host
+ verify(appWidgetHost).setListener(eq(1), appWidgetHostListenerCaptor.capture())
+ val appWidgetHostListener = appWidgetHostListenerCaptor.firstValue
+
+ // Each update should be passed to the listener
+ val providerInfo = mock<AppWidgetProviderInfo>()
+ appWidgetHostListener.onUpdateProviderInfo(providerInfo)
+ verify(listener).onUpdateProviderInfo(providerInfo)
+
+ val remoteViews = mock<RemoteViews>()
+ appWidgetHostListener.updateAppWidget(remoteViews)
+ verify(listener).updateAppWidget(remoteViews)
+
+ appWidgetHostListener.updateAppWidgetDeferred("pkg", 1)
+ verify(listener).updateAppWidgetDeferred("pkg", 1)
+
+ appWidgetHostListener.onViewDataChanged(1)
+ verify(listener).onViewDataChanged(1)
+ }
+
+ @Test
+ fun addWidget_getWidgetUpdate() =
+ testScope.runTest {
+ setupWidgets()
+
+ // Bind service
+ val binder = underTest.onBind(Intent())
+ val service = IGlanceableHubWidgetManagerService.Stub.asInterface(binder)
+
+ // Verify the update is as expected
+ val widgets by collectLastValue(service.listenForWidgetUpdates())
+ assertThat(widgets).hasSize(3)
+ assertThat(widgets?.get(0)?.has(1, "pkg_1/cls_1", 0, 3)).isTrue()
+ assertThat(widgets?.get(1)?.has(2, "pkg_2/cls_2", 1, 3)).isTrue()
+ assertThat(widgets?.get(2)?.has(3, "pkg_3/cls_3", 2, 6)).isTrue()
+
+ // Add a widget
+ service.addWidget(ComponentName("pkg_4", "cls_4"), UserHandle.of(0), 3)
+ runCurrent()
+
+ // Verify an update pushed with widget 4 added
+ assertThat(widgets).hasSize(4)
+ assertThat(widgets?.get(0)?.has(1, "pkg_1/cls_1", 0, 3)).isTrue()
+ assertThat(widgets?.get(1)?.has(2, "pkg_2/cls_2", 1, 3)).isTrue()
+ assertThat(widgets?.get(2)?.has(3, "pkg_3/cls_3", 2, 6)).isTrue()
+ assertThat(widgets?.get(3)?.has(4, "pkg_4/cls_4", 3, 3)).isTrue()
+ }
+
+ @Test
+ fun deleteWidget_getWidgetUpdate() =
+ testScope.runTest {
+ setupWidgets()
+
+ // Bind service
+ val binder = underTest.onBind(Intent())
+ val service = IGlanceableHubWidgetManagerService.Stub.asInterface(binder)
+
+ // Verify the update is as expected
+ val widgets by collectLastValue(service.listenForWidgetUpdates())
+ assertThat(widgets).hasSize(3)
+ assertThat(widgets?.get(0)?.has(1, "pkg_1/cls_1", 0, 3)).isTrue()
+ assertThat(widgets?.get(1)?.has(2, "pkg_2/cls_2", 1, 3)).isTrue()
+ assertThat(widgets?.get(2)?.has(3, "pkg_3/cls_3", 2, 6)).isTrue()
+
+ // Delete a widget
+ service.deleteWidget(1)
+ runCurrent()
+
+ // Verify an update pushed with widget 1 removed
+ assertThat(widgets).hasSize(2)
+ assertThat(widgets?.get(0)?.has(2, "pkg_2/cls_2", 1, 3)).isTrue()
+ assertThat(widgets?.get(1)?.has(3, "pkg_3/cls_3", 2, 6)).isTrue()
+ }
+
+ @Test
+ fun updateWidgetOrder_getWidgetUpdate() =
+ testScope.runTest {
+ setupWidgets()
+
+ // Bind service
+ val binder = underTest.onBind(Intent())
+ val service = IGlanceableHubWidgetManagerService.Stub.asInterface(binder)
+
+ // Verify the update is as expected
+ val widgets by collectLastValue(service.listenForWidgetUpdates())
+ assertThat(widgets).hasSize(3)
+ assertThat(widgets?.get(0)?.has(1, "pkg_1/cls_1", 0, 3)).isTrue()
+ assertThat(widgets?.get(1)?.has(2, "pkg_2/cls_2", 1, 3)).isTrue()
+ assertThat(widgets?.get(2)?.has(3, "pkg_3/cls_3", 2, 6)).isTrue()
+
+ // Update widget order
+ service.updateWidgetOrder(intArrayOf(1, 2, 3), intArrayOf(2, 1, 0))
+ runCurrent()
+
+ // Verify an update pushed with the new order
+ assertThat(widgets).hasSize(3)
+ assertThat(widgets?.get(0)?.has(3, "pkg_3/cls_3", 0, 6)).isTrue()
+ assertThat(widgets?.get(1)?.has(2, "pkg_2/cls_2", 1, 3)).isTrue()
+ assertThat(widgets?.get(2)?.has(1, "pkg_1/cls_1", 2, 3)).isTrue()
+ }
+
+ @Test
+ fun resizeWidget_getWidgetUpdate() =
+ testScope.runTest {
+ setupWidgets()
+
+ // Bind service
+ val binder = underTest.onBind(Intent())
+ val service = IGlanceableHubWidgetManagerService.Stub.asInterface(binder)
+
+ // Verify the update is as expected
+ val widgets by collectLastValue(service.listenForWidgetUpdates())
+ assertThat(widgets).hasSize(3)
+ assertThat(widgets?.get(0)?.has(1, "pkg_1/cls_1", 0, 3)).isTrue()
+ assertThat(widgets?.get(1)?.has(2, "pkg_2/cls_2", 1, 3)).isTrue()
+ assertThat(widgets?.get(2)?.has(3, "pkg_3/cls_3", 2, 6)).isTrue()
+
+ // Resize widget 1 from spanY 3 to 6
+ service.resizeWidget(1, 6, intArrayOf(1, 2, 3), intArrayOf(0, 1, 2))
+ runCurrent()
+
+ // Verify an update pushed with the new size for widget 1
+ assertThat(widgets).hasSize(3)
+ assertThat(widgets?.get(0)?.has(1, "pkg_1/cls_1", 0, 6)).isTrue()
+ assertThat(widgets?.get(1)?.has(2, "pkg_2/cls_2", 1, 3)).isTrue()
+ assertThat(widgets?.get(2)?.has(3, "pkg_3/cls_3", 2, 6)).isTrue()
+ }
+
+ private fun setupWidgets() {
+ widgetRepository.addWidget(
+ appWidgetId = 1,
+ componentName = "pkg_1/cls_1",
+ rank = 0,
+ spanY = 3,
+ )
+ widgetRepository.addWidget(
+ appWidgetId = 2,
+ componentName = "pkg_2/cls_2",
+ rank = 1,
+ spanY = 3,
+ )
+ widgetRepository.addWidget(
+ appWidgetId = 3,
+ componentName = "pkg_3/cls_3",
+ rank = 2,
+ spanY = 6,
+ )
+ }
+
+ private fun IGlanceableHubWidgetManagerService.listenForWidgetUpdates() =
+ conflatedCallbackFlow<List<CommunalWidgetContentModel>> {
+ val listener =
+ object : IGlanceableHubWidgetsListener.Stub() {
+ override fun onWidgetsUpdated(widgets: List<CommunalWidgetContentModel>) {
+ trySend(widgets)
+ }
+ }
+ addWidgetsListener(listener)
+ awaitClose { removeWidgetsListener(listener) }
+ }
+
+ private fun CommunalWidgetContentModel.has(
+ appWidgetId: Int,
+ componentName: String,
+ rank: Int,
+ spanY: Int,
+ ): Boolean {
+ return this is CommunalWidgetContentModel.Available &&
+ this.appWidgetId == appWidgetId &&
+ this.providerInfo.provider.flattenToString() == componentName &&
+ this.rank == rank &&
+ this.spanY == spanY
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/WidgetConfigurationControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/WidgetConfigurationControllerTest.kt
index 55fafdff795f..5d4eaf07be25 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/WidgetConfigurationControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/WidgetConfigurationControllerTest.kt
@@ -22,6 +22,7 @@ import androidx.activity.ComponentActivity
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.communal.shared.model.fakeGlanceableHubMultiUserHelper
import com.android.systemui.kosmos.testDispatcher
import com.android.systemui.kosmos.testScope
import com.android.systemui.testKosmos
@@ -55,7 +56,12 @@ class WidgetConfigurationControllerTest : SysuiTestCase() {
fun setUp() {
MockitoAnnotations.initMocks(this)
underTest =
- WidgetConfigurationController(ownerActivity, appWidgetHost, kosmos.testDispatcher)
+ WidgetConfigurationController(
+ ownerActivity,
+ { appWidgetHost },
+ kosmos.testDispatcher,
+ kosmos.fakeGlanceableHubMultiUserHelper,
+ )
}
@Test
@@ -68,7 +74,7 @@ class WidgetConfigurationControllerTest : SysuiTestCase() {
eq(123),
anyInt(),
eq(WidgetConfigurationController.REQUEST_CODE),
- any()
+ any(),
)
)
.thenThrow(ActivityNotFoundException())
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractorTest.kt
index 9ca3ce6929b0..e9e3e1b37776 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractorTest.kt
@@ -81,7 +81,7 @@ class FromDreamingTransitionInteractorTest(flags: FlagsParameterization?) : Sysu
this.fakeKeyguardTransitionRepository =
FakeKeyguardTransitionRepository(
// This test sends transition steps manually in the test cases.
- sendTransitionStepsOnStartTransition = false,
+ initiallySendTransitionStepsOnStartTransition = false,
testScope = testScope,
)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorTest.kt
index 9c2e6313a1f0..b29a5f4e456f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorTest.kt
@@ -27,15 +27,13 @@ import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectValues
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
-import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepositorySpy
+import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
import com.android.systemui.keyguard.data.repository.keyguardOcclusionRepository
-import com.android.systemui.keyguard.data.repository.keyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.StatusBarState
import com.android.systemui.keyguard.shared.model.StatusBarState.KEYGUARD
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
-import com.android.systemui.keyguard.util.KeyguardTransitionRepositorySpySubject.Companion.assertThat as assertThatRepository
import com.android.systemui.kosmos.testScope
import com.android.systemui.shade.data.repository.FlingInfo
import com.android.systemui.shade.data.repository.fakeShadeRepository
@@ -48,6 +46,8 @@ import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito.reset
+import org.mockito.Mockito.spy
+import com.android.systemui.keyguard.util.KeyguardTransitionRepositorySpySubject.Companion.assertThat as assertThatRepository
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@@ -55,7 +55,9 @@ import org.mockito.Mockito.reset
class FromLockscreenTransitionInteractorTest : SysuiTestCase() {
private val kosmos =
testKosmos().apply {
- this.keyguardTransitionRepository = fakeKeyguardTransitionRepositorySpy
+ this.fakeKeyguardTransitionRepository = spy(FakeKeyguardTransitionRepository(
+ testScope = testScope,
+ ))
}
private val testScope = kosmos.testScope
@@ -66,7 +68,7 @@ class FromLockscreenTransitionInteractorTest : SysuiTestCase() {
@Before
fun setup() {
- transitionRepository = kosmos.fakeKeyguardTransitionRepositorySpy
+ transitionRepository = kosmos.fakeKeyguardTransitionRepository
}
@Test
@@ -302,4 +304,74 @@ class FromLockscreenTransitionInteractorTest : SysuiTestCase() {
to = KeyguardState.LOCKSCREEN,
)
}
+
+ /**
+ * External signals can cause us to transition from PRIMARY_BOUNCER -> * while a manual
+ * transition is in progress. This test was added after a bug that caused the manual transition
+ * ID to get stuck in this scenario, preventing subsequent transitions to PRIMARY_BOUNCER.
+ */
+ @Test
+ fun testExternalTransitionAwayFromBouncer_transitionIdNotStuck() =
+ testScope.runTest {
+ underTest.start()
+ keyguardRepository.setStatusBarState(StatusBarState.KEYGUARD)
+ keyguardRepository.setKeyguardDismissible(false)
+ shadeRepository.setLegacyShadeTracking(true)
+ keyguardRepository.setKeyguardOccluded(false)
+ runCurrent()
+
+ reset(transitionRepository)
+
+ // Disable automatic sending of transition steps so we can send steps through RUNNING
+ // to simulate a cancellation.
+ transitionRepository.sendTransitionStepsOnStartTransition = false
+ shadeRepository.setLegacyShadeExpansion(0.5f)
+ runCurrent()
+
+ assertThatRepository(transitionRepository)
+ .startedTransition(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.PRIMARY_BOUNCER,
+ )
+
+ // Partially transition to PRIMARY_BOUNCER.
+ transitionRepository.sendTransitionSteps(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.PRIMARY_BOUNCER,
+ throughTransitionState = TransitionState.RUNNING,
+ testScope = testScope,
+ )
+
+ // Start a transition to GONE, which will cancel LS -> BOUNCER.
+ transitionRepository.sendTransitionSteps(
+ from = KeyguardState.PRIMARY_BOUNCER,
+ to = KeyguardState.GONE,
+ testScope = testScope,
+ )
+
+ // Go to AOD, then LOCKSCREEN.
+ transitionRepository.sendTransitionSteps(
+ from = KeyguardState.GONE,
+ to = KeyguardState.AOD,
+ testScope = testScope,
+ )
+ transitionRepository.sendTransitionSteps(
+ from = KeyguardState.AOD,
+ to = KeyguardState.LOCKSCREEN,
+ testScope = testScope,
+ )
+
+ reset(transitionRepository)
+
+ // Start a swipe up to the bouncer, and verify that we started a transition to
+ // PRIMARY_BOUNCER, verifying the transition ID did not get stuck.
+ shadeRepository.setLegacyShadeExpansion(0.25f)
+ runCurrent()
+
+ assertThatRepository(transitionRepository)
+ .startedTransition(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.PRIMARY_BOUNCER,
+ )
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/composefragment/viewmodel/AbstractQSFragmentComposeViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/composefragment/viewmodel/AbstractQSFragmentComposeViewModelTest.kt
index 2e2894d43b12..b5fc52f7a8d6 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/composefragment/viewmodel/AbstractQSFragmentComposeViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/composefragment/viewmodel/AbstractQSFragmentComposeViewModelTest.kt
@@ -24,12 +24,16 @@ import com.android.systemui.SysuiTestCase
import com.android.systemui.kosmos.testDispatcher
import com.android.systemui.kosmos.testScope
import com.android.systemui.lifecycle.activateIn
+import com.android.systemui.media.controls.domain.pipeline.legacyMediaDataManagerImpl
+import com.android.systemui.media.controls.domain.pipeline.mediaDataManager
+import com.android.systemui.qs.composefragment.dagger.usingMediaInComposeFragment
import com.android.systemui.testKosmos
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.TestResult
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.resetMain
+import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import kotlinx.coroutines.test.setMain
import org.junit.After
@@ -39,7 +43,7 @@ import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
@OptIn(ExperimentalCoroutinesApi::class)
abstract class AbstractQSFragmentComposeViewModelTest : SysuiTestCase() {
- protected val kosmos = testKosmos()
+ protected val kosmos = testKosmos().apply { mediaDataManager = legacyMediaDataManagerImpl }
protected val lifecycleOwner =
TestLifecycleOwner(
@@ -62,11 +66,15 @@ abstract class AbstractQSFragmentComposeViewModelTest : SysuiTestCase() {
}
protected inline fun TestScope.testWithinLifecycle(
- crossinline block: suspend TestScope.() -> TestResult
+ usingMedia: Boolean = true,
+ crossinline block: suspend TestScope.() -> TestResult,
): TestResult {
return runTest {
+ kosmos.usingMediaInComposeFragment = usingMedia
+
lifecycleOwner.setCurrentState(Lifecycle.State.RESUMED)
underTest.activateIn(kosmos.testScope)
+ runCurrent()
block().also { lifecycleOwner.setCurrentState(Lifecycle.State.DESTROYED) }
}
}
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 3b00f86c8f6a..9fe9ed2bf47a 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
@@ -25,6 +25,15 @@ import androidx.test.filters.SmallTest
import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.kosmos.testScope
+import com.android.systemui.media.controls.domain.pipeline.legacyMediaDataManagerImpl
+import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager
+import com.android.systemui.media.controls.ui.controller.mediaCarouselController
+import com.android.systemui.media.controls.ui.view.MediaHostState
+import com.android.systemui.media.controls.ui.view.qqsMediaHost
+import com.android.systemui.media.controls.ui.view.qsMediaHost
+import com.android.systemui.qs.composefragment.viewmodel.MediaState.ACTIVE_MEDIA
+import com.android.systemui.qs.composefragment.viewmodel.MediaState.ANY_MEDIA
+import com.android.systemui.qs.composefragment.viewmodel.MediaState.NO_MEDIA
import com.android.systemui.qs.fgsManagerController
import com.android.systemui.qs.panels.domain.interactor.tileSquishinessInteractor
import com.android.systemui.res.R
@@ -35,9 +44,11 @@ import com.android.systemui.statusbar.disableflags.data.repository.fakeDisableFl
import com.android.systemui.statusbar.sysuiStatusBarStateController
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.kotlin.whenever
@SmallTest
@RunWith(AndroidJUnit4::class)
@@ -185,6 +196,92 @@ class QSFragmentComposeViewModelTest : AbstractQSFragmentComposeViewModelTest()
}
}
+ @Test
+ fun qqsMediaHost_initializedCorrectly() =
+ with(kosmos) {
+ testScope.testWithinLifecycle {
+ assertThat(underTest.qqsMediaHost.location)
+ .isEqualTo(MediaHierarchyManager.LOCATION_QQS)
+ assertThat(underTest.qqsMediaHost.expansion).isEqualTo(MediaHostState.EXPANDED)
+ assertThat(underTest.qqsMediaHost.showsOnlyActiveMedia).isTrue()
+ assertThat(underTest.qqsMediaHost.hostView).isNotNull()
+ }
+ }
+
+ @Test
+ fun qsMediaHost_initializedCorrectly() =
+ with(kosmos) {
+ testScope.testWithinLifecycle {
+ assertThat(underTest.qsMediaHost.location)
+ .isEqualTo(MediaHierarchyManager.LOCATION_QS)
+ assertThat(underTest.qsMediaHost.expansion).isEqualTo(MediaHostState.EXPANDED)
+ assertThat(underTest.qsMediaHost.showsOnlyActiveMedia).isFalse()
+ assertThat(underTest.qsMediaHost.hostView).isNotNull()
+ }
+ }
+
+ @Test
+ fun qqsMediaVisible_onlyWhenActiveMedia() =
+ with(kosmos) {
+ testScope.testWithinLifecycle {
+ whenever(mediaCarouselController.isLockedAndHidden()).thenReturn(false)
+
+ assertThat(underTest.qqsMediaVisible).isEqualTo(underTest.qqsMediaHost.visible)
+
+ setMediaState(NO_MEDIA)
+ assertThat(underTest.qqsMediaVisible).isFalse()
+
+ setMediaState(ANY_MEDIA)
+ assertThat(underTest.qqsMediaVisible).isFalse()
+
+ setMediaState(ACTIVE_MEDIA)
+ assertThat(underTest.qqsMediaVisible).isTrue()
+ }
+ }
+
+ @Test
+ fun qsMediaVisible_onAnyMedia() =
+ with(kosmos) {
+ testScope.testWithinLifecycle {
+ whenever(mediaCarouselController.isLockedAndHidden()).thenReturn(false)
+
+ assertThat(underTest.qsMediaVisible).isEqualTo(underTest.qsMediaHost.visible)
+
+ setMediaState(NO_MEDIA)
+ assertThat(underTest.qsMediaVisible).isFalse()
+
+ setMediaState(ANY_MEDIA)
+ assertThat(underTest.qsMediaVisible).isTrue()
+
+ setMediaState(ACTIVE_MEDIA)
+ assertThat(underTest.qsMediaVisible).isTrue()
+ }
+ }
+
+ @Test
+ fun notUsingMedia_mediaNotVisible() =
+ with(kosmos) {
+ testScope.testWithinLifecycle(usingMedia = false) {
+ setMediaState(ACTIVE_MEDIA)
+
+ assertThat(underTest.qqsMediaVisible).isFalse()
+ assertThat(underTest.qsMediaVisible).isFalse()
+ }
+ }
+
+ private fun TestScope.setMediaState(state: MediaState) {
+ with(kosmos) {
+ val activeMedia = state == ACTIVE_MEDIA
+ val anyMedia = state != NO_MEDIA
+ whenever(legacyMediaDataManagerImpl.hasActiveMediaOrRecommendation())
+ .thenReturn(activeMedia)
+ whenever(legacyMediaDataManagerImpl.hasAnyMediaOrRecommendation()).thenReturn(anyMedia)
+ qqsMediaHost.updateViewVisibility()
+ qsMediaHost.updateViewVisibility()
+ }
+ runCurrent()
+ }
+
companion object {
private const val QS_DISABLE_FLAG = StatusBarManager.DISABLE2_QUICK_SETTINGS
@@ -195,3 +292,9 @@ class QSFragmentComposeViewModelTest : AbstractQSFragmentComposeViewModelTest()
private const val epsilon = 0.001f
}
}
+
+private enum class MediaState {
+ ACTIVE_MEDIA,
+ ANY_MEDIA,
+ NO_MEDIA,
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/QuickQuickSettingsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/QuickQuickSettingsViewModelTest.kt
index 2c894f9aa20f..ab5a049c91f1 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/QuickQuickSettingsViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/QuickQuickSettingsViewModelTest.kt
@@ -20,21 +20,25 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository
-import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testCase
import com.android.systemui.kosmos.testScope
+import com.android.systemui.lifecycle.activateIn
import com.android.systemui.qs.panels.domain.interactor.qsPreferencesInteractor
import com.android.systemui.qs.pipeline.domain.interactor.currentTilesInteractor
import com.android.systemui.qs.pipeline.shared.TileSpec
import com.android.systemui.res.R
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
+@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWith(AndroidJUnit4::class)
class QuickQuickSettingsViewModelTest : SysuiTestCase() {
@@ -65,7 +69,8 @@ class QuickQuickSettingsViewModelTest : SysuiTestCase() {
fakeConfigurationRepository.onConfigurationChange()
}
- private val underTest = kosmos.quickQuickSettingsViewModel
+ private val underTest =
+ kosmos.quickQuickSettingsViewModelFactory.create().apply { activateIn(kosmos.testScope) }
@Before
fun setUp() {
@@ -77,17 +82,15 @@ class QuickQuickSettingsViewModelTest : SysuiTestCase() {
with(kosmos) {
testScope.runTest {
setRows(2)
- val columns by collectLastValue(underTest.columns)
- val tileViewModels by collectLastValue(underTest.tileViewModels)
- assertThat(columns).isEqualTo(4)
+ assertThat(underTest.columns).isEqualTo(4)
// All tiles in 4 columns
// [1] [2] [3 3]
// [4] [5 5]
// [6 6] [7] [8]
// [9 9]
- assertThat(tileViewModels!!.map { it.tile.spec }).isEqualTo(tiles.take(5))
+ assertThat(underTest.tileViewModels.map { it.tile.spec }).isEqualTo(tiles.take(5))
}
}
@@ -96,10 +99,8 @@ class QuickQuickSettingsViewModelTest : SysuiTestCase() {
with(kosmos) {
testScope.runTest {
setRows(2)
- val columns by collectLastValue(underTest.columns)
- val tileViewModels by collectLastValue(underTest.tileViewModels)
- assertThat(columns).isEqualTo(4)
+ assertThat(underTest.columns).isEqualTo(4)
// All tiles in 4 columns
// [1] [2] [3 3]
// [4] [5 5]
@@ -107,9 +108,9 @@ class QuickQuickSettingsViewModelTest : SysuiTestCase() {
// [9 9]
setRows(3)
- assertThat(tileViewModels!!.map { it.tile.spec }).isEqualTo(tiles.take(8))
+ assertThat(underTest.tileViewModels.map { it.tile.spec }).isEqualTo(tiles.take(8))
setRows(1)
- assertThat(tileViewModels!!.map { it.tile.spec }).isEqualTo(tiles.take(3))
+ assertThat(underTest.tileViewModels.map { it.tile.spec }).isEqualTo(tiles.take(3))
}
}
@@ -118,10 +119,8 @@ class QuickQuickSettingsViewModelTest : SysuiTestCase() {
with(kosmos) {
testScope.runTest {
setRows(2)
- val columns by collectLastValue(underTest.columns)
- val tileViewModels by collectLastValue(underTest.tileViewModels)
- assertThat(columns).isEqualTo(4)
+ assertThat(underTest.columns).isEqualTo(4)
// All tiles in 4 columns
// [1] [2] [3 3]
// [4] [5 5]
@@ -130,8 +129,9 @@ class QuickQuickSettingsViewModelTest : SysuiTestCase() {
// Remove tile small:4
currentTilesInteractor.removeTiles(setOf(tiles[3]))
+ runCurrent()
- assertThat(tileViewModels!!.map { it.tile.spec })
+ assertThat(underTest.tileViewModels.map { it.tile.spec })
.isEqualTo(
listOf(
"$PREFIX_SMALL:1",
@@ -149,12 +149,15 @@ class QuickQuickSettingsViewModelTest : SysuiTestCase() {
currentTilesInteractor.setTiles(tiles)
}
- private fun Kosmos.setRows(rows: Int) {
- testCase.context.orCreateTestableResources.addOverride(
- R.integer.quick_qs_paginated_grid_num_rows,
- rows,
- )
- fakeConfigurationRepository.onConfigurationChange()
+ private fun TestScope.setRows(rows: Int) {
+ with(kosmos) {
+ testCase.context.orCreateTestableResources.addOverride(
+ R.integer.quick_qs_paginated_grid_num_rows,
+ rows,
+ )
+ fakeConfigurationRepository.onConfigurationChange()
+ }
+ runCurrent()
}
private companion object {
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 2a196c6b979f..1b3f29a6f0c5 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
@@ -32,7 +32,7 @@ import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.kotlin.verify
-@EnableFlags(StatusBarSimpleFragment.FLAG_NAME)
+@EnableFlags(StatusBarConnectedDisplays.FLAG_NAME)
@SmallTest
@RunWith(AndroidJUnit4::class)
class CommandQueueInitializerTest : SysuiTestCase() {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt
index c0057431f536..657e9df0029b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt
@@ -58,6 +58,7 @@ import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.withArgCaptor
import com.android.systemui.util.time.SystemClock
import com.android.systemui.wmshell.BubblesManager
+import com.google.android.msdl.domain.MSDLPlayer
import java.util.Optional
import junit.framework.Assert
import org.junit.After
@@ -110,6 +111,7 @@ class ExpandableNotificationRowControllerTest : SysuiTestCase() {
private val dismissibilityProvider: NotificationDismissibilityProvider = mock()
private val statusBarService: IStatusBarService = mock()
private val uiEventLogger: UiEventLogger = mock()
+ private val msdlPlayer: MSDLPlayer = mock()
private lateinit var controller: ExpandableNotificationRowController
@Before
@@ -150,7 +152,8 @@ class ExpandableNotificationRowControllerTest : SysuiTestCase() {
dragController,
dismissibilityProvider,
statusBarService,
- uiEventLogger
+ uiEventLogger,
+ msdlPlayer,
)
whenever(view.childrenContainer).thenReturn(childrenContainer)
@@ -268,14 +271,14 @@ class ExpandableNotificationRowControllerTest : SysuiTestCase() {
controller.mSettingsListener.onSettingChanged(
BUBBLES_SETTING_URI,
view.entry.sbn.userId,
- "1"
+ "1",
)
verify(childView).setBubblesEnabledForUser(true)
controller.mSettingsListener.onSettingChanged(
BUBBLES_SETTING_URI,
view.entry.sbn.userId,
- "9"
+ "9",
)
verify(childView).setBubblesEnabledForUser(false)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
index b2794d8f9b56..0eb620343e6a 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
@@ -804,6 +804,13 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
}
@Test
+ public void onBackPressedResetsLeaveOnKeyguardHide() {
+ when(mPrimaryBouncerInteractor.isFullyShowing()).thenReturn(true);
+ mStatusBarKeyguardViewManager.onBackPressed();
+ verify(mStatusBarStateController).setLeaveOpenOnKeyguardHide(false);
+ }
+
+ @Test
public void testResetHideBouncerWhenShowingIsFalse_alternateBouncerHides() {
// GIVEN the keyguard is showing
reset(mAlternateBouncerInteractor);
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManagerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManagerTest.kt
index 48c2cc7f11c4..a00858842742 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManagerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManagerTest.kt
@@ -20,6 +20,8 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.compose.animation.scene.ObservableTransitionState
import com.android.systemui.SysuiTestCase
+import com.android.systemui.communal.data.repository.fakeCommunalSceneRepository
+import com.android.systemui.communal.shared.model.CommunalScenes
import com.android.systemui.flags.DisableSceneContainer
import com.android.systemui.flags.EnableSceneContainer
import com.android.systemui.kosmos.testScope
@@ -113,4 +115,21 @@ class StatusBarTouchableRegionManagerTest : SysuiTestCase() {
assertThat(underTest.shouldMakeEntireScreenTouchable()).isFalse()
}
+
+ @Test
+ @DisableSceneContainer
+ fun entireScreenTouchable_communalVisible() =
+ testScope.runTest {
+ assertThat(underTest.shouldMakeEntireScreenTouchable()).isFalse()
+
+ kosmos.fakeCommunalSceneRepository.snapToScene(CommunalScenes.Communal)
+ runCurrent()
+
+ assertThat(underTest.shouldMakeEntireScreenTouchable()).isTrue()
+
+ kosmos.fakeCommunalSceneRepository.snapToScene(CommunalScenes.Blank)
+ runCurrent()
+
+ assertThat(underTest.shouldMakeEntireScreenTouchable()).isFalse()
+ }
}
diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml
index 9ec61063691a..c091cbf6c062 100644
--- a/packages/SystemUI/res/values-af/strings.xml
+++ b/packages/SystemUI/res/values-af/strings.xml
@@ -112,6 +112,8 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Neem jou skerm op?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Neem een app op"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Neem hele skerm op"</string>
+ <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+ <skip />
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Wanneer jy jou hele skerm opneem, word enigiets wat op jou skerm wys, opgeneem. Wees dus versigtig met dinge soos wagwoorde, betalingbesonderhede, boodskappe, foto’s, en oudio en video."</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Wanneer jy ’n app opneem, word enigiets wat in daardie app gewys of gespeel word, opgeneem. Wees dus versigtig met dinge soos wagwoorde, betalingbesonderhede, boodskappe, foto’s, en oudio en video."</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Neem skerm op"</string>
@@ -1413,9 +1415,15 @@
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Huidige app"</string>
<string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Toeganklikheid"</string>
<string name="shortcut_helper_title" msgid="8567500639300970049">"Kortpadsleutels"</string>
+ <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <skip />
<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>
+ <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <skip />
<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>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Sleephandvatsel"</string>
diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml
index 598e224c6a0e..af2971b2ec81 100644
--- a/packages/SystemUI/res/values-am/strings.xml
+++ b/packages/SystemUI/res/values-am/strings.xml
@@ -112,6 +112,8 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"ማያ ገፅዎን ይቀዳሉ?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"አንድ መተግበሪያ ቅዳ"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"መላው ማያ ገፅን ቅረጽ"</string>
+ <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+ <skip />
<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>
@@ -1413,9 +1415,15 @@
<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>
+ <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <skip />
<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>
+ <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <skip />
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"መዘርጊያ አዶ"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ወይም"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"መያዣ ይጎትቱ"</string>
diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index 342c8c2980d9..4ebac5a86ec1 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -112,6 +112,8 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"هل تريد تسجيل محتوى الشاشة؟"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"تسجيل محتوى تطبيق واحد"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"تسجيل محتوى الشاشة بالكامل"</string>
+ <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+ <skip />
<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>
@@ -1413,9 +1415,15 @@
<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>
+ <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <skip />
<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>
+ <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <skip />
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"رمز التوسيع"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"أو"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"مقبض السحب"</string>
diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml
index b71db2e8528c..85517f3865d2 100644
--- a/packages/SystemUI/res/values-as/strings.xml
+++ b/packages/SystemUI/res/values-as/strings.xml
@@ -112,6 +112,8 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"আপোনাৰ স্ক্ৰীনখন ৰেকৰ্ড কৰিবনে?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"এটা এপ্ ৰেকৰ্ড কৰক"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"গোটেই স্ক্ৰীনখন ৰেকৰ্ড কৰক"</string>
+ <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+ <skip />
<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>
@@ -581,10 +583,8 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"আটাইবোৰ মচক"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"পৰিচালনা কৰক"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"ইতিহাস"</string>
- <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
- <skip />
- <!-- no translation found for notification_history_button_description (1578657591405033383) -->
- <skip />
+ <string name="notification_settings_button_description" msgid="2441994740884163889">"জাননীৰ ছেটিং"</string>
+ <string name="notification_history_button_description" msgid="1578657591405033383">"জাননীৰ ইতিহাস"</string>
<string name="notification_section_header_incoming" msgid="850925217908095197">"নতুন"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"নীৰৱ"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"জাননীসমূহ"</string>
@@ -1413,9 +1413,15 @@
<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>
+ <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <skip />
<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>
+ <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <skip />
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"বিস্তাৰ কৰাৰ চিহ্ন"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"অথবা"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"ড্ৰেগ হেণ্ডেল"</string>
diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml
index d51c9aa3af3d..f08724a81449 100644
--- a/packages/SystemUI/res/values-az/strings.xml
+++ b/packages/SystemUI/res/values-az/strings.xml
@@ -112,6 +112,8 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Ekran qeydə alınsın?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Bir tətbiqi qeydə alın"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Bütün ekranı qeydə alın"</string>
+ <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+ <skip />
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Bütün ekranı qeydə alarkən ekranda göstərilən bütün kontent qeydə alınır. Parol, ödəniş detalları, mesaj, foto, habelə audio və video kimi məlumatlarla bağlı diqqətli olun."</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Tətbiq qeydə aldıqda həmin tətbiqdə göstərilən və ya işə salınan bütün kontent qeydə alınır. Parol, ödəniş detalları, mesaj, foto, habelə audio və video kimi məlumatlarla bağlı diqqətli olun."</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Ekranı qeydə alın"</string>
@@ -1413,9 +1415,15 @@
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Cari tətbiq"</string>
<string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Xüsusi imkanlar"</string>
<string name="shortcut_helper_title" msgid="8567500639300970049">"Klaviatura qısayolları"</string>
+ <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <skip />
<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>
+ <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <skip />
<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>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Dəstəyi çəkin"</string>
diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
index 881495f0d3fe..f5096915e261 100644
--- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml
+++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
@@ -112,6 +112,8 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Želite da snimite ekran?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Snimi jednu aplikaciju"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Snimi ceo ekran"</string>
+ <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+ <skip />
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Kada snimate ceo ekran, snima se sve što je na njemu. Zato pazite na lozinke, informacije o plaćanju, poruke, slike, audio i video sadržaj."</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Kada snimate aplikaciju, snima se sav sadržaj koji se prikazuje ili pušta u njoj. Zato pazite na lozinke, informacije o plaćanju, poruke, slike, audio i video sadržaj."</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Snimi ekran"</string>
@@ -581,10 +583,8 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"Obriši sve"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Upravljaj"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Istorija"</string>
- <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
- <skip />
- <!-- no translation found for notification_history_button_description (1578657591405033383) -->
- <skip />
+ <string name="notification_settings_button_description" msgid="2441994740884163889">"Podešavanja obaveštenja"</string>
+ <string name="notification_history_button_description" msgid="1578657591405033383">"Istorija obaveštenja"</string>
<string name="notification_section_header_incoming" msgid="850925217908095197">"Novo"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"Nečujno"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Obaveštenja"</string>
@@ -1413,9 +1413,15 @@
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Aktuelna aplikacija"</string>
<string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Pristupačnost"</string>
<string name="shortcut_helper_title" msgid="8567500639300970049">"Tasterske prečice"</string>
+ <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <skip />
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Prečice pretrage"</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>
+ <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <skip />
<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>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Marker za prevlačenje"</string>
diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml
index 16bb70cf62ca..bc0e2d189c02 100644
--- a/packages/SystemUI/res/values-be/strings.xml
+++ b/packages/SystemUI/res/values-be/strings.xml
@@ -112,6 +112,8 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Запісаць экран?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Запісаць адну праграму"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Запісаць змесціва ўсяго экрана"</string>
+ <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+ <skip />
<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>
@@ -1413,9 +1415,15 @@
<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>
+ <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <skip />
<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>
+ <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <skip />
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Значок \"Разгарнуць\""</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"або"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Маркер перацягвання"</string>
diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml
index 2b3a0b9383bf..2ee1f5f0f21e 100644
--- a/packages/SystemUI/res/values-bg/strings.xml
+++ b/packages/SystemUI/res/values-bg/strings.xml
@@ -112,6 +112,8 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Да се записва ли екранът?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Записване на едно приложение"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Записване на целия екран"</string>
+ <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+ <skip />
<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>
@@ -1413,9 +1415,15 @@
<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>
+ <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <skip />
<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>
+ <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <skip />
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Икона за разгъване"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"или"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Манипулатор за преместване с плъзгане"</string>
diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml
index f39a644d5e88..c593210c4613 100644
--- a/packages/SystemUI/res/values-bn/strings.xml
+++ b/packages/SystemUI/res/values-bn/strings.xml
@@ -112,6 +112,8 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"আপনার স্ক্রিন রেকর্ড করবেন?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"একটি অ্যাপ রেকর্ড করুন"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"সম্পূর্ণ স্ক্রিন রেকর্ড করুন"</string>
+ <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+ <skip />
<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>
@@ -581,10 +583,8 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"সব মুছে দিন"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"ম্যানেজ করুন"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"ইতিহাস"</string>
- <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
- <skip />
- <!-- no translation found for notification_history_button_description (1578657591405033383) -->
- <skip />
+ <string name="notification_settings_button_description" msgid="2441994740884163889">"বিজ্ঞপ্তি সেটিংস"</string>
+ <string name="notification_history_button_description" msgid="1578657591405033383">"বিজ্ঞপ্তির ইতিহাস"</string>
<string name="notification_section_header_incoming" msgid="850925217908095197">"নতুন"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"আওয়াজ করবে না"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"বিজ্ঞপ্তি"</string>
@@ -1413,9 +1413,15 @@
<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>
+ <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <skip />
<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>
+ <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <skip />
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"আইকন বড় করুন"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"অথবা"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"টেনে আনার হ্যান্ডেল"</string>
diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml
index ec57c6abede1..38f42659245e 100644
--- a/packages/SystemUI/res/values-bs/strings.xml
+++ b/packages/SystemUI/res/values-bs/strings.xml
@@ -112,6 +112,8 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Snimati ekran?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Snimaj jednu aplikaciju"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Snimaj cijeli ekran"</string>
+ <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+ <skip />
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Kada snimate cijeli ekran, snimat će se sve što se prikazuje na ekranu. Stoga budite oprezni s informacijama kao što su lozinke, podaci o plaćanju, poruke, fotografije, zvukovi i videozapisi."</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Kada snimate aplikaciju, snimat će se sve što se prikazuje ili reproducira u toj aplikaciji. Stoga budite oprezni s informacijama kao što su lozinke, podaci o plaćanju, poruke, fotografije, zvukovi i videozapisi."</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Snimaj ekran"</string>
@@ -581,10 +583,8 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"Obriši sve"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Upravljajte"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Historija"</string>
- <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
- <skip />
- <!-- no translation found for notification_history_button_description (1578657591405033383) -->
- <skip />
+ <string name="notification_settings_button_description" msgid="2441994740884163889">"Postavke obavještenja"</string>
+ <string name="notification_history_button_description" msgid="1578657591405033383">"Historija obavještenja"</string>
<string name="notification_section_header_incoming" msgid="850925217908095197">"Novo"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"Nečujno"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Obavještenja"</string>
@@ -1413,9 +1413,15 @@
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Trenutna aplikacija"</string>
<string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Pristupačnost"</string>
<string name="shortcut_helper_title" msgid="8567500639300970049">"Prečice tastature"</string>
+ <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <skip />
<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>
+ <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <skip />
<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>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Ručica za prevlačenje"</string>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index 9c9ba90a225d..bcaca5ae16e9 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -112,6 +112,8 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Vols gravar la pantalla?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Grava una aplicació"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Grava tota la pantalla"</string>
+ <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+ <skip />
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Quan graves tota la pantalla, es grava tot el que es mostra en pantalla. Per aquest motiu, ves amb compte amb elements com les contrasenyes, les dades de pagament, els missatges, les fotos, i l\'àudio i el vídeo."</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Quan graves una aplicació, es grava tot el que es mostra o es reprodueix en aquesta aplicació. Per aquest motiu, ves amb compte amb les contrasenyes, les dades de pagament, els missatges, les fotos, i l\'àudio i el vídeo."</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Grava la pantalla"</string>
@@ -1413,9 +1415,15 @@
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Aplicació actual"</string>
<string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Accessibilitat"</string>
<string name="shortcut_helper_title" msgid="8567500639300970049">"Tecles de drecera"</string>
+ <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <skip />
<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>
+ <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <skip />
<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>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Ansa per arrossegar"</string>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index 95f369988c1e..76ae86deddd4 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -112,6 +112,8 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Pořídit nahrávku obrazovky?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Nahrát jednu aplikaci"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Nahrát celou obrazovku"</string>
+ <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+ <skip />
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Při nahrávání celé obrazovky se zaznamenává veškerý obsah na obrazovce. Buďte proto opatrní, když jde o hesla, platební údaje, zprávy, fotografie, zvuk a video."</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Při nahrávání aplikace se zaznamenává všechno, co se v dané obrazovce zobrazuje nebo přehrává. Buďte proto opatrní, když jde o hesla, platební údaje, zprávy, fotografie, zvuk a video."</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Nahrát obrazovku"</string>
@@ -1413,9 +1415,15 @@
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Aktuální aplikace"</string>
<string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Přístupnost"</string>
<string name="shortcut_helper_title" msgid="8567500639300970049">"Klávesové zkratky"</string>
+ <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <skip />
<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>
+ <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <skip />
<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>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Úchyt pro přetažení"</string>
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index 8266799a081d..abcd4b8d45ce 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -112,6 +112,8 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Vil du optage din skærm?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Optag én app"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Optag hele skærmen"</string>
+ <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+ <skip />
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Når du optager hele skærmen, bliver alt det, der vises på skærmen, optaget. Vær derfor forsigtig med ting såsom adgangskoder, betalingsoplysninger, beskeder, billeder, lyd og video."</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Når du optager en app, optages alt det, der vises eller afspilles i den pågældende app. Vær derfor forsigtig med ting såsom adgangskoder, betalingsoplysninger, beskeder, billeder, lyd og video."</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Optag skærm"</string>
@@ -581,10 +583,8 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"Ryd alle"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Administrer"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Historik"</string>
- <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
- <skip />
- <!-- no translation found for notification_history_button_description (1578657591405033383) -->
- <skip />
+ <string name="notification_settings_button_description" msgid="2441994740884163889">"Indstillinger for notifikationer"</string>
+ <string name="notification_history_button_description" msgid="1578657591405033383">"Notifikationshistorik"</string>
<string name="notification_section_header_incoming" msgid="850925217908095197">"Nye"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"Lydløs"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Notifikationer"</string>
@@ -1413,9 +1413,15 @@
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Aktuel app"</string>
<string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Hjælpefunktioner"</string>
<string name="shortcut_helper_title" msgid="8567500639300970049">"Tastaturgenveje"</string>
+ <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <skip />
<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>
+ <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <skip />
<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>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Håndtag"</string>
diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml
index a4bc364be58b..9799a932bcd9 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -112,6 +112,8 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Bildschirm aufnehmen?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Einzelne App aufnehmen"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Gesamten Bildschirm aufnehmen"</string>
+ <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+ <skip />
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Wenn du den gesamten Bildschirm aufnimmst, ist in der Aufnahme alles zu sehen, was auf dem Bildschirm angezeigt wird. Sei also vorsichtig mit Informationen wie Passwörtern, Zahlungsdetails, Nachrichten, Fotos sowie Audio- und Videoinhalten."</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Wenn du eine App aufnimmst, ist in der Aufnahme alles zu sehen, was in dieser App angezeigt oder abgespielt wird. Sei also vorsichtig mit Informationen wie Passwörtern, Zahlungsdetails, Nachrichten, Fotos sowie Audio- und Videoinhalten."</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Bildschirm aufnehmen"</string>
@@ -1413,9 +1415,15 @@
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Aktuelle App"</string>
<string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Bedienungshilfen"</string>
<string name="shortcut_helper_title" msgid="8567500639300970049">"Tastenkürzel"</string>
+ <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <skip />
<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>
+ <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <skip />
<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>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Ziehpunkt"</string>
diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml
index 278a8ea7cfeb..7200a91b618e 100644
--- a/packages/SystemUI/res/values-el/strings.xml
+++ b/packages/SystemUI/res/values-el/strings.xml
@@ -112,6 +112,8 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Να γίνει εγγραφή της οθόνης σας;"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Εγγραφή μίας εφαρμογής"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Εγγραφή ολόκληρης της οθόνης"</string>
+ <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+ <skip />
<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>
@@ -1413,9 +1415,15 @@
<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>
+ <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <skip />
<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>
+ <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <skip />
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Εικονίδιο ανάπτυξης"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ή"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Λαβή μεταφοράς"</string>
diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml
index 2fbd60a11faf..1ecf4f17cac3 100644
--- a/packages/SystemUI/res/values-en-rAU/strings.xml
+++ b/packages/SystemUI/res/values-en-rAU/strings.xml
@@ -112,6 +112,8 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Record your screen?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Record one app"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Record entire screen"</string>
+ <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+ <skip />
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"When you\'re recording your entire screen, anything displayed on your screen is recorded. So, be careful with things like passwords, payment details, messages, photos, audio and video."</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"When you\'re recording an app, anything displayed or played in that app is recorded. So, be careful with things like passwords, payment details, messages, photos, audio and video."</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Record screen"</string>
@@ -1413,9 +1415,15 @@
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Current app"</string>
<string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Accessibility"</string>
<string name="shortcut_helper_title" msgid="8567500639300970049">"Keyboard shortcuts"</string>
+ <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <skip />
<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>
+ <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <skip />
<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>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Drag handle"</string>
diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml
index 23475fab120c..f775513ca418 100644
--- a/packages/SystemUI/res/values-en-rCA/strings.xml
+++ b/packages/SystemUI/res/values-en-rCA/strings.xml
@@ -112,6 +112,8 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Record your screen?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Record one app"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Record entire screen"</string>
+ <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+ <skip />
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"When you’re recording your entire screen, anything shown on your screen is recorded. So be careful with things like passwords, payment details, messages, photos, and audio and video."</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"When you’re recording an app, anything shown or played in that app is recorded. So be careful with things like passwords, payment details, messages, photos, and audio and video."</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Record screen"</string>
@@ -136,17 +138,14 @@
<string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"You\'re currently recording <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Stop recording"</string>
<string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Sharing screen"</string>
- <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
- <skip />
+ <string name="share_to_app_chip_accessibility_label_generic" msgid="5517431657924536133">"Sharing content"</string>
<string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Stop sharing screen?"</string>
- <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
- <skip />
+ <string name="share_to_app_stop_dialog_title_generic" msgid="9079161538135843648">"Stop sharing?"</string>
<string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"You\'re currently sharing your entire screen with <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>"</string>
<string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"You\'re currently sharing your entire screen with an app"</string>
<string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"You\'re currently sharing <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>"</string>
<string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"You\'re currently sharing an app"</string>
- <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
- <skip />
+ <string name="share_to_app_stop_dialog_message_generic" msgid="7622174291691249392">"You\'re currently sharing with an app"</string>
<string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Stop sharing"</string>
<string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Casting screen"</string>
<string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Stop casting?"</string>
@@ -521,10 +520,8 @@
<string name="communal_widget_picker_title" msgid="1953369090475731663">"Lock screen widgets"</string>
<string name="communal_widget_picker_description" msgid="490515450110487871">"Anyone can view widgets on your lock screen, even if your tablet\'s locked."</string>
<string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"unselect widget"</string>
- <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
- <skip />
- <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
- <skip />
+ <string name="accessibility_action_label_shrink_widget" msgid="8259511040536438771">"Decrease height"</string>
+ <string name="accessibility_action_label_expand_widget" msgid="9190524260912211759">"Increase height"</string>
<string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Lock screen widgets"</string>
<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>
@@ -1411,9 +1408,15 @@
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Current App"</string>
<string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Accessibility"</string>
<string name="shortcut_helper_title" msgid="8567500639300970049">"Keyboard shortcuts"</string>
+ <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <skip />
<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>
+ <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <skip />
<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>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Drag handle"</string>
diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml
index 2fbd60a11faf..1ecf4f17cac3 100644
--- a/packages/SystemUI/res/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/strings.xml
@@ -112,6 +112,8 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Record your screen?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Record one app"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Record entire screen"</string>
+ <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+ <skip />
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"When you\'re recording your entire screen, anything displayed on your screen is recorded. So, be careful with things like passwords, payment details, messages, photos, audio and video."</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"When you\'re recording an app, anything displayed or played in that app is recorded. So, be careful with things like passwords, payment details, messages, photos, audio and video."</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Record screen"</string>
@@ -1413,9 +1415,15 @@
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Current app"</string>
<string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Accessibility"</string>
<string name="shortcut_helper_title" msgid="8567500639300970049">"Keyboard shortcuts"</string>
+ <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <skip />
<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>
+ <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <skip />
<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>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Drag handle"</string>
diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml
index 2fbd60a11faf..1ecf4f17cac3 100644
--- a/packages/SystemUI/res/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res/values-en-rIN/strings.xml
@@ -112,6 +112,8 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Record your screen?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Record one app"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Record entire screen"</string>
+ <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+ <skip />
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"When you\'re recording your entire screen, anything displayed on your screen is recorded. So, be careful with things like passwords, payment details, messages, photos, audio and video."</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"When you\'re recording an app, anything displayed or played in that app is recorded. So, be careful with things like passwords, payment details, messages, photos, audio and video."</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Record screen"</string>
@@ -1413,9 +1415,15 @@
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Current app"</string>
<string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Accessibility"</string>
<string name="shortcut_helper_title" msgid="8567500639300970049">"Keyboard shortcuts"</string>
+ <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <skip />
<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>
+ <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <skip />
<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>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Drag handle"</string>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index 9ea154020d73..77c2a639f903 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -112,6 +112,8 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"¿Quieres grabar la pantalla?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Grabar una app"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Grabar toda la pantalla"</string>
+ <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+ <skip />
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Cuando grabes toda la pantalla, se grabará todo lo que se muestre en ella. Por lo tanto, debes tener cuidado con contraseñas, detalles de pagos, mensajes, fotos, audios y videos."</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Cuando grabes una app, se registrará todo lo que se muestre o reproduzca en ella. Por lo tanto, debes tener cuidado con contraseñas, detalles de pagos, mensajes, fotos, audios y videos."</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Grabar pantalla"</string>
@@ -581,10 +583,8 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"Cerrar todo"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Administrar"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Historial"</string>
- <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
- <skip />
- <!-- no translation found for notification_history_button_description (1578657591405033383) -->
- <skip />
+ <string name="notification_settings_button_description" msgid="2441994740884163889">"Configuración de notificaciones"</string>
+ <string name="notification_history_button_description" msgid="1578657591405033383">"Historial de notificaciones"</string>
<string name="notification_section_header_incoming" msgid="850925217908095197">"Nuevo"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"Silenciadas"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Notificaciones"</string>
@@ -1413,9 +1413,15 @@
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"App actual"</string>
<string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Accesibilidad"</string>
<string name="shortcut_helper_title" msgid="8567500639300970049">"Combinaciones de teclas"</string>
+ <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <skip />
<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>
+ <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <skip />
<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>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Controlador de arrastre"</string>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index 16ffd44a044c..81b1562ed533 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -112,6 +112,8 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"¿Grabar la pantalla?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Grabar una aplicación"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Grabar toda la pantalla"</string>
+ <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+ <skip />
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Cuando grabas toda la pantalla, se graba todo lo que se muestre en ella. Debes tener cuidado con elementos como contraseñas, detalles de pagos, mensajes, fotos, audio y vídeo."</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Cuando grabas una aplicación, se graba todo lo que se muestre o reproduzca en ella. Debes tener cuidado con elementos como contraseñas, detalles de pagos, mensajes, fotos, audio y vídeo."</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Grabar pantalla"</string>
@@ -1413,9 +1415,15 @@
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Aplicación en uso"</string>
<string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Accesibilidad"</string>
<string name="shortcut_helper_title" msgid="8567500639300970049">"Combinaciones de teclas"</string>
+ <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <skip />
<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>
+ <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <skip />
<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>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Controlador de arrastre"</string>
diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml
index 5c9664b84d66..56039da5acc8 100644
--- a/packages/SystemUI/res/values-et/strings.xml
+++ b/packages/SystemUI/res/values-et/strings.xml
@@ -112,6 +112,8 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Kas salvestada ekraanikuvast video?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Ühe rakenduse salvestamine"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Kogu ekraanikuva salvestamine"</string>
+ <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+ <skip />
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Kui salvestate kogu ekraani, salvestatakse kõik ekraanil kuvatud andmed. Seega olge ettevaatlik selliste andmetega nagu paroolid, makseteave, sõnumid, fotod ning heli ja video."</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Kui salvestate rakendust, salvestatakse kõik, mida selles rakenduses näidatakse või esitatakse. Seega olge ettevaatlik selliste andmetega nagu paroolid, makseteave, sõnumid, fotod ning heli ja video."</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Ekraanikuva jäädvustamine"</string>
@@ -1413,9 +1415,15 @@
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Praegune rakendus"</string>
<string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Juurdepääsetavus"</string>
<string name="shortcut_helper_title" msgid="8567500639300970049">"Klaviatuuri otseteed"</string>
+ <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <skip />
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Otsingu otseteed"</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>
+ <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <skip />
<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>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Lohistamispide"</string>
diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml
index 31348b61484f..426d1d754c94 100644
--- a/packages/SystemUI/res/values-eu/strings.xml
+++ b/packages/SystemUI/res/values-eu/strings.xml
@@ -112,6 +112,8 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Pantaila grabatu nahi duzu?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Grabatu aplikazio bat"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Grabatu pantaila osoa"</string>
+ <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+ <skip />
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Pantaila osoa grabatzen ari zarenean, pantailan agertzen den guztia grabatzen da. Beraz, kontuz ibili pasahitzekin, ordainketen xehetasunekin, mezuekin, argazkiekin, audioekin eta bideoekin, besteak beste."</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Aplikazio bat grabatzen ari zarenean, aplikazio horretan agertzen den edo bertan erreproduzitzen ari den guztia grabatzen da. Beraz, kontuz ibili pasahitzekin, ordainketen xehetasunekin, mezuekin, argazkiekin, audioekin eta bideoekin, besteak beste."</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Grabatu pantaila"</string>
@@ -1413,9 +1415,15 @@
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Oraingo aplikazioa"</string>
<string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Erabilerraztasuna"</string>
<string name="shortcut_helper_title" msgid="8567500639300970049">"Lasterbideak"</string>
+ <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <skip />
<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>
+ <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <skip />
<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>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Arrastatzeko kontrol-puntua"</string>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index bc89200599cb..b0d23aa81898 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -112,6 +112,8 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"صفحه‌نمایش ضبط شود؟"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"ضبط یک برنامه"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"ضبط کل صفحه‌نمایش"</string>
+ <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+ <skip />
<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>
@@ -581,10 +583,8 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"پاک کردن همه موارد"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"مدیریت"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"سابقه"</string>
- <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
- <skip />
- <!-- no translation found for notification_history_button_description (1578657591405033383) -->
- <skip />
+ <string name="notification_settings_button_description" msgid="2441994740884163889">"تنظیمات اعلان"</string>
+ <string name="notification_history_button_description" msgid="1578657591405033383">"سابقه اعلان"</string>
<string name="notification_section_header_incoming" msgid="850925217908095197">"جدید"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"بی‌صدا"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"اعلان‌ها"</string>
@@ -1413,9 +1413,15 @@
<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>
+ <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <skip />
<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>
+ <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <skip />
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"نماد ازهم بازکردن"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"یا"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"دستگیره کشاندن"</string>
diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml
index a40cbaecfaa5..690228f9d333 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -112,6 +112,8 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Tallennetaanko näytön toimintaa?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Tallenna yhdestä sovelluksesta"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Tallenna koko näyttö"</string>
+ <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+ <skip />
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Kun tallennat koko näyttöä, kaikki näytöllä näkyvä sisältö tallennetaan. Ole siis varovainen, kun lisäät salasanoja, maksutietoja, viestejä, kuvia, audiota tai videoita."</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Kun tallennat sovellusta, kaikki sovelluksessa näkyvä tai toistettu sisältö tallennetaan. Ole siis varovainen, kun lisäät salasanoja, maksutietoja, viestejä, kuvia, audiota tai videoita."</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Tallenna näyttö"</string>
@@ -1413,9 +1415,15 @@
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Nykyinen sovellus"</string>
<string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Saavutettavuus"</string>
<string name="shortcut_helper_title" msgid="8567500639300970049">"Pikanäppäimet"</string>
+ <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <skip />
<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>
+ <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <skip />
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Laajennuskuvake"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"tai"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Vetokahva"</string>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index 288be348f670..5ea58ce1e574 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -112,6 +112,8 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Enregistrer votre écran?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Enregistrer une appli"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Enregistrer l\'écran entier"</string>
+ <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+ <skip />
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Lorsque vous enregistrez l\'intégralité de votre écran, tout ce qui s\'affiche sur votre écran est enregistré. Par conséquent, soyez prudent avec les mots de passe, les détails du mode de paiement, les messages, les photos et les contenus audio et vidéo."</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Lorsque vous enregistrez une appli, tout ce qui est affiché ou lu dans cette appli est enregistré. Par conséquent, soyez prudent avec les mots de passe, les détails du mode de paiement, les messages, les photos et les contenus audio et vidéo."</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Enregistrer l\'écran"</string>
@@ -1413,9 +1415,15 @@
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Appli actuelle"</string>
<string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Accessibilité"</string>
<string name="shortcut_helper_title" msgid="8567500639300970049">"Raccourcis-clavier"</string>
+ <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <skip />
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Recherchez 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>
+ <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <skip />
<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>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Poignée de déplacement"</string>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index 9d9f3f99708c..2be31b11c1f5 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -112,6 +112,8 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Enregistrer l\'écran ?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Enregistrer une appli"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Enregistrer tout l\'écran"</string>
+ <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+ <skip />
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Lorsque vous enregistrez l\'intégralité de votre écran, tout ce qui s\'y affiche est enregistré. Faites donc attention aux éléments tels que les mots de passe, les détails du mode de paiement, les messages, les photos, et les contenus audio et vidéo."</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Lorsque vous enregistrez une appli, tout ce qui est affiché ou lu dans celle-ci est enregistré. Faites donc attention aux éléments tels que les mots de passe, détails de mode de paiement, messages, photos et contenus audio et vidéo."</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Enregistrer l\'écran"</string>
@@ -1413,9 +1415,15 @@
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Appli actuelle"</string>
<string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Accessibilité"</string>
<string name="shortcut_helper_title" msgid="8567500639300970049">"Raccourcis clavier"</string>
+ <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <skip />
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Raccourcis de recherche"</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>
+ <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <skip />
<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>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Poignée de déplacement"</string>
diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml
index 1430bc2a1131..2bd0b30988e7 100644
--- a/packages/SystemUI/res/values-gl/strings.xml
+++ b/packages/SystemUI/res/values-gl/strings.xml
@@ -112,6 +112,8 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Queres gravar a túa pantalla?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Gravar unha aplicación"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Gravar pantalla completa"</string>
+ <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+ <skip />
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Cando gravas a pantalla completa, recóllese todo o que se mostra nela. Recomendámosche que teñas coidado con determinada información, como os contrasinais, os detalles de pago, as mensaxes e as fotos, así como co contido de audio e de vídeo."</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Cando gravas unha aplicación, recóllese todo o que se mostra ou reproduce nela. Recomendámosche que teñas coidado con determinada información, como os contrasinais, os detalles de pago, as mensaxes e as fotos, así como co contido de audio e de vídeo."</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Gravar pantalla"</string>
@@ -1413,9 +1415,15 @@
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Aplicación actual"</string>
<string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Accesibilidade"</string>
<string name="shortcut_helper_title" msgid="8567500639300970049">"Atallos de teclado"</string>
+ <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <skip />
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Atallos de busca"</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>
+ <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <skip />
<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>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Controlador de arrastre"</string>
diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml
index bc2739f3a9dc..becca8f8b37f 100644
--- a/packages/SystemUI/res/values-gu/strings.xml
+++ b/packages/SystemUI/res/values-gu/strings.xml
@@ -112,6 +112,8 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"તમારી સ્ક્રીન રેકોર્ડ કરીએ?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"એક ઍપ રેકોર્ડ કરો"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"પૂર્ણ સ્ક્રીન રેકોર્ડ કરો"</string>
+ <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+ <skip />
<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>
@@ -581,10 +583,8 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"બધુ સાફ કરો"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"મેનેજ કરો"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"ઇતિહાસ"</string>
- <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
- <skip />
- <!-- no translation found for notification_history_button_description (1578657591405033383) -->
- <skip />
+ <string name="notification_settings_button_description" msgid="2441994740884163889">"નોટિફિકેશનના સેટિંગ"</string>
+ <string name="notification_history_button_description" msgid="1578657591405033383">"નોટિફિકેશનનો ઇતિહાસ"</string>
<string name="notification_section_header_incoming" msgid="850925217908095197">"નવા"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"સાઇલન્ટ"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"નોટિફિકેશન"</string>
@@ -1413,9 +1413,15 @@
<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>
+ <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <skip />
<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>
+ <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <skip />
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"\'મોટું કરો\'નું આઇકન"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"અથવા"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"ઑબ્જેક્ટ ખેંચવાનું હૅન્ડલ"</string>
diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index f9a1fa5f84ad..a6fc05e2c7e0 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/strings.xml
@@ -112,6 +112,8 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"क्या आपको स्क्रीन रिकॉर्ड करनी है?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"एक ऐप्लिकेशन की रिकॉर्डिंग करें"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"पूरी स्क्रीन रिकॉर्ड करें"</string>
+ <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+ <skip />
<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>
@@ -1413,9 +1415,15 @@
<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>
+ <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <skip />
<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>
+ <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <skip />
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"बड़ा करने का आइकॉन"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"या"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"खींचकर छोड़ने वाला हैंडल"</string>
diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml
index 4a17fdc37b8e..cb7a1931be4d 100644
--- a/packages/SystemUI/res/values-hr/strings.xml
+++ b/packages/SystemUI/res/values-hr/strings.xml
@@ -112,6 +112,8 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Želite li snimati zaslon?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Snimanje jedne aplikacije"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Snimanje cijelog zaslona"</string>
+ <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+ <skip />
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Kad snimate cijeli zaslon, snima se sve što se prikazuje na zaslonu. Stoga pazite na stvari kao što su zaporke, podaci o plaćanju, poruke, fotografije te audio i videozapisi."</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Kad snimate aplikaciju, snima se sve što se prikazuje ili reproducira u toj aplikaciji. Stoga pazite na stvari kao što su zaporke, podaci o plaćanju, poruke, fotografije te audio i videozapisi."</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Snimanje zaslona"</string>
@@ -1413,9 +1415,15 @@
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Trenutačna aplikacija"</string>
<string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Pristupačnost"</string>
<string name="shortcut_helper_title" msgid="8567500639300970049">"Tipkovni prečaci"</string>
+ <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <skip />
<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>
+ <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <skip />
<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>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Marker za povlačenje"</string>
diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml
index ff5339ff5488..b09947fc2600 100644
--- a/packages/SystemUI/res/values-hu/strings.xml
+++ b/packages/SystemUI/res/values-hu/strings.xml
@@ -112,6 +112,8 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Rögzíti a képernyőt?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Egyetlen alkalmazás rögzítése"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Teljes képernyő rögzítése"</string>
+ <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+ <skip />
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"A teljes képernyő rögzítése esetén a képernyőn megjelenő minden tartalom rögzítésre kerül. Ezért legyen elővigyázatos a jelszavakkal, a fizetési adatokkal, az üzenetekkel, a fotókkal, valamint a hang- és videófelvételekkel."</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Alkalmazás rögzítésekor az adott alkalmazásban megjelenített vagy lejátszott minden tartalom rögzítésre kerül. Ezért legyen elővigyázatos a jelszavakkal, a fizetési adatokkal, az üzenetekkel, a fotókkal, valamint a hang- és videófelvételekkel."</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Képernyő rögzítése"</string>
@@ -1413,9 +1415,15 @@
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Jelenlegi alkalmazás"</string>
<string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Kisegítő lehetőségek"</string>
<string name="shortcut_helper_title" msgid="8567500639300970049">"Billentyűparancsok"</string>
+ <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <skip />
<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>
+ <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <skip />
<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>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Fogópont"</string>
diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml
index 8fb13478da16..174b52622fd2 100644
--- a/packages/SystemUI/res/values-hy/strings.xml
+++ b/packages/SystemUI/res/values-hy/strings.xml
@@ -112,6 +112,8 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Տեսագրե՞լ ձեր էկրանը"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Տեսագրել մեկ հավելված"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Տեսագրել ամբողջ էկրանը"</string>
+ <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+ <skip />
<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>
@@ -1413,9 +1415,15 @@
<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>
+ <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <skip />
<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>
+ <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <skip />
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ծավալել պատկերակը"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"կամ"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Տեղափոխման նշիչ"</string>
diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index 0b316befc259..e706b272458a 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -112,6 +112,8 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Rekam layar Anda?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Rekam satu aplikasi"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Rekam seluruh layar"</string>
+ <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+ <skip />
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Saat Anda merekam seluruh layar, semua hal yang ditampilkan di layar akan direkam. Jadi, berhati-hatilah saat memasukkan sandi, detail pembayaran, pesan, foto, audio, dan video."</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Jika Anda merekam aplikasi, semua hal yang ditampilkan atau diputar di aplikasi tersebut akan direkam. Jadi, berhati-hatilah saat memasukkan sandi, detail pembayaran, pesan, foto, audio, dan video."</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Rekam layar"</string>
@@ -409,7 +411,7 @@
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Mode satu tangan"</string>
<string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Alat bantu dengar"</string>
<string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Aktif"</string>
- <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Terputus"</string>
+ <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Tidak terhubung"</string>
<string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Alat bantu dengar"</string>
<string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Sambungkan perangkat baru"</string>
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Klik untuk menyambungkan perangkat baru"</string>
@@ -1190,7 +1192,7 @@
<string name="media_output_dialog_group" msgid="5571251347877452212">"Grup"</string>
<string name="media_output_dialog_single_device" msgid="3102758980643351058">"1 perangkat dipilih"</string>
<string name="media_output_dialog_multiple_devices" msgid="1093771040315422350">"<xliff:g id="COUNT">%1$d</xliff:g> perangkat dipilih"</string>
- <string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(terputus)"</string>
+ <string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(tidak terhubung)"</string>
<string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"Tidak dapat beralih. Ketuk untuk mencoba lagi."</string>
<string name="media_output_dialog_pairing_new" msgid="5098212763195577270">"Hubungkan perangkat"</string>
<string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"Buka aplikasi untuk mentransmisikan sesi ini."</string>
@@ -1413,9 +1415,15 @@
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Aplikasi Saat Ini"</string>
<string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Aksesibilitas"</string>
<string name="shortcut_helper_title" msgid="8567500639300970049">"Pintasan keyboard"</string>
+ <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <skip />
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Pintasan penelusuran"</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>
+ <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <skip />
<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>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Handel geser"</string>
diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml
index 700ae589073c..267291069366 100644
--- a/packages/SystemUI/res/values-is/strings.xml
+++ b/packages/SystemUI/res/values-is/strings.xml
@@ -112,6 +112,8 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Viltu taka upp skjáinn?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Taka upp eitt forrit"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Taka upp allan skjáinn"</string>
+ <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+ <skip />
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Þegar þú tekur upp allan skjáinn verður allt sem er sýnilegt á skjánum tekið upp. Passaðu því upp á aðgangsorð, greiðsluupplýsingar, skilaboð, myndir, hljóð og vídeó."</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Þegar þú tekur upp forrit verður allt sem er sýnilegt eða spilað í forritinu tekið upp. Passaðu því upp á aðgangsorð, greiðsluupplýsingar, skilaboð, myndir, hljóð og vídeó."</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Taka upp skjá"</string>
@@ -581,10 +583,8 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"Hreinsa allt"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Stjórna"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Ferill"</string>
- <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
- <skip />
- <!-- no translation found for notification_history_button_description (1578657591405033383) -->
- <skip />
+ <string name="notification_settings_button_description" msgid="2441994740884163889">"Tilkynningastillingar"</string>
+ <string name="notification_history_button_description" msgid="1578657591405033383">"Tilkynningaferill"</string>
<string name="notification_section_header_incoming" msgid="850925217908095197">"Nýtt"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"Hljóðlaust"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Tilkynningar"</string>
@@ -1413,9 +1413,15 @@
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Núverandi forrit"</string>
<string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Aðgengi"</string>
<string name="shortcut_helper_title" msgid="8567500639300970049">"Flýtilyklar"</string>
+ <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <skip />
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Leitarflýtileiðir"</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>
+ <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <skip />
<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>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Dragkló"</string>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index e7734fa4a38d..fd56c5b69f5a 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -112,6 +112,8 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Registrare lo schermo?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Registra un\'app"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Registra l\'intero schermo"</string>
+ <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+ <skip />
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Quando registri l\'intero schermo, tutto ciò che viene mostrato sullo schermo viene registrato. Presta quindi attenzione a password, dati di pagamento, messaggi, foto, audio e video."</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Quando registri un\'app, tutto ciò che viene mostrato o riprodotto al suo interno viene registrato. Presta quindi attenzione a password, dati di pagamento, messaggi, foto, audio e video."</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Registra lo schermo"</string>
@@ -136,17 +138,14 @@
<string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Al momento stai registrando <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Interrompi registrazione"</string>
<string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Condivisione dello schermo in corso"</string>
- <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
- <skip />
+ <string name="share_to_app_chip_accessibility_label_generic" msgid="5517431657924536133">"Condivisione di contenuti in corso…"</string>
<string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Vuoi interrompere la condivisione dello schermo?"</string>
- <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
- <skip />
+ <string name="share_to_app_stop_dialog_title_generic" msgid="9079161538135843648">"Interrompere la condivisione?"</string>
<string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Al momento stai condividendo l\'intero schermo con <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>"</string>
<string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Al momento stai condividendo l\'intero schermo con un\'app"</string>
<string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Al momento stai condividendo <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>"</string>
<string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Al momento stai condividendo un\'app"</string>
- <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
- <skip />
+ <string name="share_to_app_stop_dialog_message_generic" msgid="7622174291691249392">"Al momento stai condividendo contenuti con un\'app"</string>
<string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Interrompi condivisione"</string>
<string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Trasmissione dello schermo in corso…"</string>
<string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Vuoi interrompere la trasmissione?"</string>
@@ -521,10 +520,8 @@
<string name="communal_widget_picker_title" msgid="1953369090475731663">"Widget della schermata di blocco"</string>
<string name="communal_widget_picker_description" msgid="490515450110487871">"Chiunque può visualizzare i widget sulla tua schermata di blocco, anche se il tablet è bloccato."</string>
<string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"deseleziona widget"</string>
- <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
- <skip />
- <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
- <skip />
+ <string name="accessibility_action_label_shrink_widget" msgid="8259511040536438771">"Riduci altezza"</string>
+ <string name="accessibility_action_label_expand_widget" msgid="9190524260912211759">"Aumenta altezza"</string>
<string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Widget della schermata di blocco"</string>
<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>
@@ -1413,9 +1410,15 @@
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"App corrente"</string>
<string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Accessibilità"</string>
<string name="shortcut_helper_title" msgid="8567500639300970049">"Scorciatoie da tastiera"</string>
+ <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <skip />
<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>
+ <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <skip />
<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>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Punto di trascinamento"</string>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index 553eaae83da9..384c680b116f 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -112,6 +112,8 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"להקליט את המסך?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"הקלטה של אפליקציה אחת"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"הקלטה של כל המסך"</string>
+ <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+ <skip />
<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>
@@ -581,10 +583,8 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"ניקוי הכול"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"ניהול"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"היסטוריה"</string>
- <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
- <skip />
- <!-- no translation found for notification_history_button_description (1578657591405033383) -->
- <skip />
+ <string name="notification_settings_button_description" msgid="2441994740884163889">"הגדרות של התראות"</string>
+ <string name="notification_history_button_description" msgid="1578657591405033383">"היסטוריית ההתראות"</string>
<string name="notification_section_header_incoming" msgid="850925217908095197">"התראות חדשות"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"מצב שקט"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"התראות"</string>
@@ -1413,9 +1413,15 @@
<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>
+ <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <skip />
<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>
+ <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <skip />
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"סמל ההרחבה"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"או"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"נקודת האחיזה לגרירה"</string>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index a6fffee06695..a0a3b2bdd403 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -112,6 +112,8 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"画面を録画しますか?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"1 つのアプリを録画"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"画面全体を録画"</string>
+ <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+ <skip />
<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>
@@ -1411,9 +1413,15 @@
<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>
+ <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <skip />
<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>
+ <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <skip />
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"開くアイコン"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"または"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"ドラッグ ハンドル"</string>
diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml
index 9608253c1e1e..471957e044cb 100644
--- a/packages/SystemUI/res/values-ka/strings.xml
+++ b/packages/SystemUI/res/values-ka/strings.xml
@@ -112,6 +112,8 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"გსურთ თქვენი ეკრანის ჩაწერა?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"ერთი აპის ჩაწერა"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"მთლიანი ეკრანის ჩაწერა"</string>
+ <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+ <skip />
<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>
@@ -1413,9 +1415,15 @@
<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>
+ <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <skip />
<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>
+ <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <skip />
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"ხატულის გაფართოება"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ან"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"სახელური ჩავლებისთვის"</string>
diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml
index 0456fa8164fc..3dbfd86b3f38 100644
--- a/packages/SystemUI/res/values-kk/strings.xml
+++ b/packages/SystemUI/res/values-kk/strings.xml
@@ -112,6 +112,8 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Қолданба экранын жазасыз ба?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Бір қолданба экранын жазу"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Бүкіл экранды жазу"</string>
+ <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+ <skip />
<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>
@@ -1413,9 +1415,15 @@
<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>
+ <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <skip />
<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>
+ <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <skip />
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Жаю белгішесі"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"немесе"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Сүйрейтін тетік"</string>
diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml
index 3c33531717bc..b2c8977ab7ce 100644
--- a/packages/SystemUI/res/values-km/strings.xml
+++ b/packages/SystemUI/res/values-km/strings.xml
@@ -112,6 +112,8 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"ថត​អេក្រង់​របស់អ្នកឬ?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"ថត​កម្មវិធី​ទោល"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"ថតអេក្រង់ទាំងមូល"</string>
+ <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+ <skip />
<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>
@@ -1413,9 +1415,15 @@
<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>
+ <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <skip />
<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>
+ <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <skip />
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"រូបតំណាង \"ពង្រីក\""</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ឬ"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"ដង​អូស"</string>
diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml
index dca2e4b4a59e..20c367696858 100644
--- a/packages/SystemUI/res/values-kn/strings.xml
+++ b/packages/SystemUI/res/values-kn/strings.xml
@@ -112,6 +112,8 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"ನಿಮ್ಮ ಸ್ಕ್ರೀನ್‌ ಅನ್ನು ರೆಕಾರ್ಡ್ ಮಾಡಬೇಕೇ?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"ಒಂದು ಆ್ಯಪ್ ಅನ್ನು ರೆಕಾರ್ಡ್ ಮಾಡಿ"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"ಸಂಪೂರ್ಣ ಸ್ಕ್ರೀನ್ ಅನ್ನು ರೆಕಾರ್ಡ್ ಮಾಡಿ"</string>
+ <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+ <skip />
<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>
@@ -581,10 +583,8 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"ಎಲ್ಲವನ್ನೂ ತೆರವುಗೊಳಿಸಿ"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"ನಿರ್ವಹಿಸಿ"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"ಇತಿಹಾಸ"</string>
- <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
- <skip />
- <!-- no translation found for notification_history_button_description (1578657591405033383) -->
- <skip />
+ <string name="notification_settings_button_description" msgid="2441994740884163889">"ನೋಟಿಫಿಕೇಶನ್ ಸೆಟ್ಟಿಂಗ್‌ಗಳು"</string>
+ <string name="notification_history_button_description" msgid="1578657591405033383">"ನೋಟಿಫಿಕೇಶನ್ ಇತಿಹಾಸ"</string>
<string name="notification_section_header_incoming" msgid="850925217908095197">"ಹೊಸತು"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"ನಿಶ್ಶಬ್ದ"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"ಅಧಿಸೂಚನೆಗಳು"</string>
@@ -1413,9 +1413,15 @@
<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>
+ <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <skip />
<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>
+ <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <skip />
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"ವಿಸ್ತೃತಗೊಳಿಸುವ ಐಕಾನ್"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ಅಥವಾ"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"ಡ್ರ್ಯಾಗ್‌ ಹ್ಯಾಂಡಲ್‌"</string>
diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml
index 8674dc3ad863..35dc245643ae 100644
--- a/packages/SystemUI/res/values-ko/strings.xml
+++ b/packages/SystemUI/res/values-ko/strings.xml
@@ -112,6 +112,8 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"화면을 녹화하시겠습니까?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"단일 앱 녹화"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"전체 화면 녹화"</string>
+ <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+ <skip />
<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>
@@ -581,10 +583,8 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"모두 지우기"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"관리"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"기록"</string>
- <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
- <skip />
- <!-- no translation found for notification_history_button_description (1578657591405033383) -->
- <skip />
+ <string name="notification_settings_button_description" msgid="2441994740884163889">"알림 설정"</string>
+ <string name="notification_history_button_description" msgid="1578657591405033383">"알림 기록"</string>
<string name="notification_section_header_incoming" msgid="850925217908095197">"새 알림"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"무음"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"알림"</string>
@@ -1413,15 +1413,21 @@
<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>
+ <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <skip />
<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>
+ <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <skip />
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"확장 아이콘"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"또는"</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="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"키보드를 사용하여 이동"</string>
- <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"단축키 알아보기"</string>
+ <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"단축키에 관해 알아보세요."</string>
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"터치패드를 사용하여 이동"</string>
<string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"터치패드 동작 알아보기"</string>
<string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"키보드와 터치패드를 사용하여 이동"</string>
diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml
index 363342a94793..72e86cc4a703 100644
--- a/packages/SystemUI/res/values-ky/strings.xml
+++ b/packages/SystemUI/res/values-ky/strings.xml
@@ -112,6 +112,8 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Экранды жаздырасызбы?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Бир колдонмону жаздыруу"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Бүтүндөй экранды жаздыруу"</string>
+ <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+ <skip />
<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>
@@ -1413,9 +1415,15 @@
<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>
+ <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <skip />
<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>
+ <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <skip />
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Жайып көрсөтүү сүрөтчөсү"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"же"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Cүйрөө маркери"</string>
diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml
index 54c2c6baf6a7..f6fe3cce059a 100644
--- a/packages/SystemUI/res/values-lo/strings.xml
+++ b/packages/SystemUI/res/values-lo/strings.xml
@@ -112,6 +112,8 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"ບັນທຶກໜ້າຈໍຂອງທ່ານບໍ?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"ບັນທຶກແອັບດຽວ"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"ບັນທຶກໝົດໜ້າຈໍ"</string>
+ <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+ <skip />
<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>
@@ -1413,9 +1415,15 @@
<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>
+ <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <skip />
<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>
+ <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <skip />
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"ໄອຄອນຂະຫຍາຍ"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ຫຼື"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"ບ່ອນຈັບລາກ"</string>
diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml
index dca26a41edbf..70a0063d7627 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -112,6 +112,8 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Įrašyti ekraną?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Įrašyti vieną programą"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Įrašyti visą ekraną"</string>
+ <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+ <skip />
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Kai įrašote visą ekraną, įrašomas visas ekrane rodomas turinys. Todėl būkite atsargūs naudodami slaptažodžius, išsamią mokėjimo metodo informaciją, pranešimus, nuotraukas ir garso bei vaizdo įrašus."</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Kai įrašote programą, įrašomas visas toje programoje rodomas ar leidžiamas turinys. Todėl būkite atsargūs naudodami slaptažodžius, išsamią mokėjimo metodo informaciją, pranešimus, nuotraukas ir garso bei vaizdo įrašus."</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Įrašyti ekraną"</string>
@@ -1413,9 +1415,15 @@
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Esama programa"</string>
<string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Pritaikomumas"</string>
<string name="shortcut_helper_title" msgid="8567500639300970049">"Spartieji klavišai"</string>
+ <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <skip />
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Paieškos šaukiniai"</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>
+ <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <skip />
<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>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Vilkimo rankenėlė"</string>
diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml
index c5ba0ad80493..360afad1c6fa 100644
--- a/packages/SystemUI/res/values-lv/strings.xml
+++ b/packages/SystemUI/res/values-lv/strings.xml
@@ -112,6 +112,8 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Vai ierakstīt ekrānu?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Ierakstīt vienu lietotni"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Ierakstīt visu ekrānu"</string>
+ <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+ <skip />
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Ierakstot visu ekrānu, viss, kas redzams ekrānā, tiek ierakstīts. Tāpēc piesardzīgi apejieties ar parolēm, maksājumu informāciju, ziņojumiem, fotoattēliem un audio un video saturu."</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Ierakstot lietotni, tiek ierakstīts viss attiecīgajā lietotnē rādītais vai atskaņotais. Tāpēc piesardzīgi apejieties ar parolēm, maksājumu informāciju, ziņojumiem, fotoattēliem un audio un video saturu."</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Ierakstīt ekrānu"</string>
@@ -1413,9 +1415,15 @@
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Pašreizējā lietotne"</string>
<string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Pieejamība"</string>
<string name="shortcut_helper_title" msgid="8567500639300970049">"Īsinājumtaustiņi"</string>
+ <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <skip />
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Meklēšanas 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>
+ <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <skip />
<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>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Vilkšanas turis"</string>
diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml
index 65e958e8d765..ebd62a9568e0 100644
--- a/packages/SystemUI/res/values-mk/strings.xml
+++ b/packages/SystemUI/res/values-mk/strings.xml
@@ -112,6 +112,8 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Да се снима екранот?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Снимање на една апликација"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Снимање на целиот екран"</string>
+ <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+ <skip />
<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>
@@ -581,10 +583,8 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"Избриши сѐ"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Управувајте"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Историја"</string>
- <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
- <skip />
- <!-- no translation found for notification_history_button_description (1578657591405033383) -->
- <skip />
+ <string name="notification_settings_button_description" msgid="2441994740884163889">"Поставки за известувања"</string>
+ <string name="notification_history_button_description" msgid="1578657591405033383">"Историја на известувања"</string>
<string name="notification_section_header_incoming" msgid="850925217908095197">"Нов"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"Безгласно"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Известувања"</string>
@@ -1413,9 +1413,15 @@
<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>
+ <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <skip />
<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>
+ <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <skip />
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Икона за проширување"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"или"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Рачка за влечење"</string>
diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml
index e10ecff4b318..6152fce2e02b 100644
--- a/packages/SystemUI/res/values-ml/strings.xml
+++ b/packages/SystemUI/res/values-ml/strings.xml
@@ -112,6 +112,8 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"നിങ്ങളുടെ സ്ക്രീൻ റെക്കോർഡ് ചെയ്യണോ?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"ഒരു ആപ്പ് റെക്കോർഡ് ചെയ്യുക"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"സ്ക്രീൻ പൂർണ്ണമായി റെക്കോർഡ് ചെയ്യുക"</string>
+ <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+ <skip />
<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>
@@ -581,10 +583,8 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"എല്ലാം മായ്‌ക്കുക"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"മാനേജ് ചെയ്യുക"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"ചരിത്രം"</string>
- <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
- <skip />
- <!-- no translation found for notification_history_button_description (1578657591405033383) -->
- <skip />
+ <string name="notification_settings_button_description" msgid="2441994740884163889">"അറിയിപ്പ് ക്രമീകരണം"</string>
+ <string name="notification_history_button_description" msgid="1578657591405033383">"അറിയിപ്പ് ചരിത്രം"</string>
<string name="notification_section_header_incoming" msgid="850925217908095197">"പുതിയത്"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"നിശബ്‌ദം"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"അറിയിപ്പുകൾ"</string>
@@ -1413,9 +1413,15 @@
<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>
+ <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <skip />
<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>
+ <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <skip />
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"വികസിപ്പിക്കൽ ഐക്കൺ"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"അല്ലെങ്കിൽ"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"വലിച്ചിടുന്നതിനുള്ള ഹാൻഡിൽ"</string>
diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml
index cb5f054cccf5..aafc8c610c55 100644
--- a/packages/SystemUI/res/values-mn/strings.xml
+++ b/packages/SystemUI/res/values-mn/strings.xml
@@ -112,6 +112,8 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Дэлгэцээ бичих үү?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Нэг аппыг бичих"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Бүтэн дэлгэцийг бичих"</string>
+ <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+ <skip />
<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>
@@ -581,10 +583,8 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"Бүгдийг арилгах"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Удирдах"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Түүх"</string>
- <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
- <skip />
- <!-- no translation found for notification_history_button_description (1578657591405033383) -->
- <skip />
+ <string name="notification_settings_button_description" msgid="2441994740884163889">"Мэдэгдлийн тохиргоо"</string>
+ <string name="notification_history_button_description" msgid="1578657591405033383">"Мэдэгдлийн түүх"</string>
<string name="notification_section_header_incoming" msgid="850925217908095197">"Шинэ"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"Чимээгүй"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Мэдэгдлүүд"</string>
@@ -1413,9 +1413,15 @@
<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>
+ <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <skip />
<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>
+ <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <skip />
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Дэлгэх дүрс тэмдэг"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"эсвэл"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Чирэх бариул"</string>
diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml
index 8aa0afaaa207..fbbb1670b286 100644
--- a/packages/SystemUI/res/values-mr/strings.xml
+++ b/packages/SystemUI/res/values-mr/strings.xml
@@ -112,6 +112,8 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"तुमची स्क्रीन रेकॉर्ड करायची आहे का?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"एक अ‍ॅप रेकॉर्ड करा"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"पूर्ण स्क्रीन रेकॉर्ड करा"</string>
+ <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+ <skip />
<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>
@@ -581,10 +583,8 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"सर्व साफ करा"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"व्यवस्थापित करा"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"इतिहास"</string>
- <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
- <skip />
- <!-- no translation found for notification_history_button_description (1578657591405033383) -->
- <skip />
+ <string name="notification_settings_button_description" msgid="2441994740884163889">"नोटिफिकेशन सेटिंग्ज"</string>
+ <string name="notification_history_button_description" msgid="1578657591405033383">"नोटिफिकेशन इतिहास"</string>
<string name="notification_section_header_incoming" msgid="850925217908095197">"नवीन"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"सायलंट"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"सूचना"</string>
@@ -1413,9 +1413,15 @@
<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>
+ <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <skip />
<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>
+ <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <skip />
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"विस्तार करा आयकन"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"किंवा"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"ड्रॅग हॅंडल"</string>
diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml
index d0071f4d73d7..087c8a664076 100644
--- a/packages/SystemUI/res/values-ms/strings.xml
+++ b/packages/SystemUI/res/values-ms/strings.xml
@@ -112,6 +112,8 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Rakam skrin anda?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Rakam satu apl"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Rakam seluruh skrin"</string>
+ <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+ <skip />
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Apabila anda merakam seluruh skrin anda, apa-apa sahaja yang dipaparkan pada skrin anda akan dirakam. Oleh hal yang demikian, berhati-hati dengan perkara seperti kata laluan, butiran pembayaran, mesej, foto dan audio serta video."</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Apabila anda merakam apl, apa-apa sahaja yang dipaparkan atau dimainkan dalam apl tersebut akan dirakam. Oleh hal yang demikian, berhati-hati dengan perkara seperti kata laluan, butiran pembayaran, mesej, foto dan audio serta video."</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Rakam skrin"</string>
@@ -1413,9 +1415,15 @@
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Apl Semasa"</string>
<string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Kebolehaksesan"</string>
<string name="shortcut_helper_title" msgid="8567500639300970049">"Pintasan papan kekunci"</string>
+ <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <skip />
<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>
+ <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <skip />
<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>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Pemegang seret"</string>
diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml
index fd4c1478f914..d456bcae7085 100644
--- a/packages/SystemUI/res/values-my/strings.xml
+++ b/packages/SystemUI/res/values-my/strings.xml
@@ -112,6 +112,8 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"ဖန်သားပြင်ကို ရိုက်ကူးမလား။"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"အက်ပ်တစ်ခုကို ရိုက်ကူးရန်"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"ဖန်သားပြင်တစ်ခုလုံးကို ရိုက်ကူးရန်"</string>
+ <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+ <skip />
<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>
@@ -1413,9 +1415,15 @@
<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>
+ <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <skip />
<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>
+ <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <skip />
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"ပိုပြရန် သင်္ကေတ"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"သို့မဟုတ်"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"ဖိဆွဲအထိန်း"</string>
diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml
index 1c30ca7a4f4c..1fbb35b0905e 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -112,6 +112,8 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Vil du ta opp skjermen?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Ta opp én app"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Ta opp hele skjermen"</string>
+ <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+ <skip />
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Når du tar opp hele skjermen, blir alt som vises på skjermen, tatt opp. Derfor bør du være forsiktig med for eksempel passord, betalingsopplysninger, meldinger, bilder, lyd og video."</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Når du tar opp en app, blir alt som vises eller spilles av i appen, tatt opp. Derfor bør du være forsiktig med for eksempel passord, betalingsopplysninger, meldinger, bilder, lyd og video."</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Ta opp skjermen"</string>
@@ -1413,9 +1415,15 @@
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Aktiv app"</string>
<string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Tilgjengelighet"</string>
<string name="shortcut_helper_title" msgid="8567500639300970049">"Hurtigtaster"</string>
+ <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <skip />
<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>
+ <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <skip />
<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>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Håndtak"</string>
diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml
index f7ac880de720..381118ac4d6c 100644
--- a/packages/SystemUI/res/values-ne/strings.xml
+++ b/packages/SystemUI/res/values-ne/strings.xml
@@ -112,6 +112,8 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"तपाईंको स्क्रिन रेकर्ड गर्ने हो?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"एउटा एप रेकर्ड गर्नुहोस्"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"पूरै स्क्रिन रेकर्ड गर्नुहोस्"</string>
+ <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+ <skip />
<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>
@@ -1413,9 +1415,15 @@
<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>
+ <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <skip />
<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>
+ <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <skip />
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"\"एक्स्पान्ड गर्नुहोस्\" आइकन"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"वा"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"ड्र्याग ह्यान्डल"</string>
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index 4cea60528e2a..8ff59ee93c98 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -112,6 +112,8 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Je scherm opnemen?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Eén app opnemen"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Hele scherm opnemen"</string>
+ <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+ <skip />
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Als je je hele scherm opneemt, wordt alles opgenomen wat op je scherm wordt getoond. Wees daarom voorzichtig met bijvoorbeeld wachtwoorden, betalingsgegevens, berichten, foto\'s, en audio en video."</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Als je een app opneemt, wordt alles opgenomen wat wordt getoond of afgespeeld in die app. Wees daarom voorzichtig met bijvoorbeeld wachtwoorden, betalingsgegevens, berichten, foto\'s, en audio en video."</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Scherm opnemen"</string>
@@ -581,10 +583,8 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"Alles wissen"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Beheren"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Geschiedenis"</string>
- <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
- <skip />
- <!-- no translation found for notification_history_button_description (1578657591405033383) -->
- <skip />
+ <string name="notification_settings_button_description" msgid="2441994740884163889">"Instellingen voor meldingen"</string>
+ <string name="notification_history_button_description" msgid="1578657591405033383">"Meldings­geschiedenis"</string>
<string name="notification_section_header_incoming" msgid="850925217908095197">"Nieuw"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"Stil"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Meldingen"</string>
@@ -1413,9 +1413,15 @@
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Huidige app"</string>
<string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Toegankelijkheid"</string>
<string name="shortcut_helper_title" msgid="8567500639300970049">"Sneltoetsen"</string>
+ <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <skip />
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Snelkoppelingen voor zoekopdrachten"</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>
+ <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <skip />
<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>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Handgreep voor slepen"</string>
diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml
index bbbea612f7f8..60864409ee7f 100644
--- a/packages/SystemUI/res/values-or/strings.xml
+++ b/packages/SystemUI/res/values-or/strings.xml
@@ -112,6 +112,8 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"ଆପଣଙ୍କ ସ୍କ୍ରିନକୁ ରେକର୍ଡ କରିବେ?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"ଗୋଟିଏ ଆପ ରେକର୍ଡ କରନ୍ତୁ"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"ସମ୍ପୂର୍ଣ୍ଣ ସ୍କ୍ରିନ ରେକର୍ଡ କରନ୍ତୁ"</string>
+ <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+ <skip />
<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>
@@ -1413,9 +1415,15 @@
<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>
+ <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <skip />
<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>
+ <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <skip />
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"ଆଇକନକୁ ବିସ୍ତାର କରନ୍ତୁ"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"କିମ୍ବା"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"ଡ୍ରାଗ ହେଣ୍ଡେଲ"</string>
diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml
index 0170a71db268..b99986a5e955 100644
--- a/packages/SystemUI/res/values-pa/strings.xml
+++ b/packages/SystemUI/res/values-pa/strings.xml
@@ -112,6 +112,8 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"ਕੀ ਤੁਹਾਡੀ ਸਕ੍ਰੀਨ ਨੂੰ ਰਿਕਾਰਡ ਕਰਨਾ ਹੈ?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"ਇੱਕ ਐਪ ਨੂੰ ਰਿਕਾਰਡ ਕਰੋ"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"ਪੂਰੀ ਸਕ੍ਰੀਨ ਨੂੰ ਰਿਕਾਰਡ ਕਰੋ"</string>
+ <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+ <skip />
<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>
@@ -581,10 +583,8 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"ਸਭ ਕਲੀਅਰ ਕਰੋ"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"ਪ੍ਰਬੰਧਨ ਕਰੋ"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"ਇਤਿਹਾਸ"</string>
- <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
- <skip />
- <!-- no translation found for notification_history_button_description (1578657591405033383) -->
- <skip />
+ <string name="notification_settings_button_description" msgid="2441994740884163889">"ਸੂਚਨਾ ਸੈਟਿੰਗਾਂ"</string>
+ <string name="notification_history_button_description" msgid="1578657591405033383">"ਸੂਚਨਾ ਇਤਿਹਾਸ"</string>
<string name="notification_section_header_incoming" msgid="850925217908095197">"ਨਵੀਆਂ"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"ਸ਼ਾਂਤ"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"ਸੂਚਨਾਵਾਂ"</string>
@@ -1413,9 +1413,15 @@
<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>
+ <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <skip />
<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>
+ <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <skip />
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"ਪ੍ਰਤੀਕ ਦਾ ਵਿਸਤਾਰ ਕਰੋ"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ਜਾਂ"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"ਘਸੀਟਣ ਵਾਲਾ ਹੈਂਡਲ"</string>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index 33a743f625d3..88177ac56bb7 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -112,6 +112,8 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Nagrywać ekran?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Nagrywaj jedną aplikację"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Nagrywaj cały ekran"</string>
+ <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+ <skip />
<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>
@@ -1413,9 +1415,15 @@
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Bieżąca aplikacja"</string>
<string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Ułatwienia dostępu"</string>
<string name="shortcut_helper_title" msgid="8567500639300970049">"Skróty klawiszowe"</string>
+ <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <skip />
<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>
+ <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <skip />
<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>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Uchwyt do przeciągania"</string>
diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml
index 8041760ae14c..e67c16856e58 100644
--- a/packages/SystemUI/res/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/res/values-pt-rBR/strings.xml
@@ -112,6 +112,8 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Gravar a tela?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Gravar um app"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Gravar a tela toda"</string>
+ <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+ <skip />
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Quando você grava a tela toda, tudo o que aparece nela é registrado. Portanto, tenha cuidado com senhas, detalhes de pagamento, mensagens, fotos, áudios e vídeos."</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Quando você grava um app, todas as informações visíveis ou abertas nele ficam registradas. Portanto, tenha cuidado com senhas, detalhes de pagamento, mensagens, fotos, áudios e vídeos."</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Gravar a tela"</string>
@@ -1413,9 +1415,15 @@
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"App atual"</string>
<string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Acessibilidade"</string>
<string name="shortcut_helper_title" msgid="8567500639300970049">"Atalhos do teclado"</string>
+ <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <skip />
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Atalhos de pesquisa"</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>
+ <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <skip />
<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>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Alça de arrastar"</string>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index f0009986b232..30e16f06f00f 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -112,6 +112,8 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Gravar o ecrã?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Gravar uma app"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Gravar o ecrã inteiro"</string>
+ <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+ <skip />
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Quando está a gravar o ecrã inteiro, tudo o que é apresentado no ecrã é gravado. Por isso, tenha cuidado com, por exemplo, palavras-passe, detalhes de pagamento, mensagens, fotos, áudio e vídeo."</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Quando está a gravar uma app, tudo o que é apresentado ou reproduzido nessa app é gravado. Por isso, tenha cuidado com, por exemplo, palavras-passe, detalhes de pagamento, mensagens, fotos, áudio e vídeo."</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Gravar ecrã"</string>
@@ -136,17 +138,14 @@
<string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Neste momento, está a gravar a app <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Parar gravação"</string>
<string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"A partilhar o ecrã"</string>
- <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
- <skip />
+ <string name="share_to_app_chip_accessibility_label_generic" msgid="5517431657924536133">"A partilhar conteúdo"</string>
<string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Parar a partilha do ecrã?"</string>
- <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
- <skip />
+ <string name="share_to_app_stop_dialog_title_generic" msgid="9079161538135843648">"Parar a partilha?"</string>
<string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Neste momento, está a partilhar todo o seu ecrã com a app <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>"</string>
<string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Neste momento, está a partilhar todo o seu ecrã com uma app"</string>
<string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Neste momento, está a partilhar a app <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>"</string>
<string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Neste momento, está a partilhar uma app"</string>
- <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
- <skip />
+ <string name="share_to_app_stop_dialog_message_generic" msgid="7622174291691249392">"Neste momento, está a partilhar com uma app"</string>
<string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Parar partilha"</string>
<string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"A transmitir o ecrã"</string>
<string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Parar a transmissão?"</string>
@@ -521,10 +520,8 @@
<string name="communal_widget_picker_title" msgid="1953369090475731663">"Widgets do ecrã de bloqueio"</string>
<string name="communal_widget_picker_description" msgid="490515450110487871">"Todos podem pode ver widgets no ecrã de bloqueio, mesmo com o tablet bloqueado."</string>
<string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"desmarcar widget"</string>
- <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
- <skip />
- <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
- <skip />
+ <string name="accessibility_action_label_shrink_widget" msgid="8259511040536438771">"Diminuir altura"</string>
+ <string name="accessibility_action_label_expand_widget" msgid="9190524260912211759">"Aumentar altura"</string>
<string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Widgets do ecrã de bloqueio"</string>
<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>
@@ -1413,9 +1410,15 @@
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"App atual"</string>
<string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Acessibilidade"</string>
<string name="shortcut_helper_title" msgid="8567500639300970049">"Atalhos de teclado"</string>
+ <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <skip />
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Atalhos de pesquisa"</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>
+ <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <skip />
<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>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Indicador para arrastar"</string>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index 8041760ae14c..e67c16856e58 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -112,6 +112,8 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Gravar a tela?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Gravar um app"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Gravar a tela toda"</string>
+ <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+ <skip />
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Quando você grava a tela toda, tudo o que aparece nela é registrado. Portanto, tenha cuidado com senhas, detalhes de pagamento, mensagens, fotos, áudios e vídeos."</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Quando você grava um app, todas as informações visíveis ou abertas nele ficam registradas. Portanto, tenha cuidado com senhas, detalhes de pagamento, mensagens, fotos, áudios e vídeos."</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Gravar a tela"</string>
@@ -1413,9 +1415,15 @@
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"App atual"</string>
<string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Acessibilidade"</string>
<string name="shortcut_helper_title" msgid="8567500639300970049">"Atalhos do teclado"</string>
+ <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <skip />
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Atalhos de pesquisa"</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>
+ <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <skip />
<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>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Alça de arrastar"</string>
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index 57a2a0c2da63..de7f8dac8dd1 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -112,6 +112,8 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Înregistrezi ecranul?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Înregistrează o aplicație"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Înregistrează tot ecranul"</string>
+ <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+ <skip />
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Când înregistrezi întregul ecran, se înregistrează tot ce apare pe ecran. Prin urmare, ai grijă cu parolele, detaliile de plată, mesajele, fotografiile și conținutul audio și video."</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Când înregistrezi o aplicație, se înregistrează tot ce se afișează sau se redă în aplicație. Prin urmare, ai grijă cu parolele, detaliile de plată, mesajele, fotografiile și conținutul audio și video."</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Înregistrează ecranul"</string>
@@ -1413,9 +1415,15 @@
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Aplicația actuală"</string>
<string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Accesibilitate"</string>
<string name="shortcut_helper_title" msgid="8567500639300970049">"Comenzi rapide de la tastatură"</string>
+ <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <skip />
<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>
+ <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <skip />
<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>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Ghidaj de tragere"</string>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index 507f70926620..a007685d4af3 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -112,6 +112,8 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Начать запись экрана?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Записывать одно приложение"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Записывать весь экран"</string>
+ <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+ <skip />
<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>
@@ -581,10 +583,8 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"Очистить все"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Настроить"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"История"</string>
- <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
- <skip />
- <!-- no translation found for notification_history_button_description (1578657591405033383) -->
- <skip />
+ <string name="notification_settings_button_description" msgid="2441994740884163889">"Настройки уведомлений"</string>
+ <string name="notification_history_button_description" msgid="1578657591405033383">"История уведомлений"</string>
<string name="notification_section_header_incoming" msgid="850925217908095197">"Новое"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"Без звука"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Уведомления"</string>
@@ -1413,9 +1413,15 @@
<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>
+ <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <skip />
<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>
+ <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <skip />
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Значок \"Развернуть\""</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"или"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Маркер перемещения"</string>
diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml
index fb9be30202db..ae9c4d0cf711 100644
--- a/packages/SystemUI/res/values-si/strings.xml
+++ b/packages/SystemUI/res/values-si/strings.xml
@@ -112,6 +112,8 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"ඔබේ තිරය පටිගත කරන්න ද?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"එක් යෙදුමක් පටිගත කරන්න"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"සම්පූර්ණ තිරය පටිගත කරන්න"</string>
+ <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+ <skip />
<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>
@@ -1413,9 +1415,15 @@
<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>
+ <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <skip />
<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>
+ <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <skip />
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"දිගහැරීම් නිරූපකය"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"හෝ"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"ඇදීම් හැඬලය"</string>
diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index 42a2613cf329..c2235667f08b 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -112,6 +112,8 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Chcete nahrávať obrazovku?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Nahrávať jednu aplikáciu"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Nahrávať celú obrazovku"</string>
+ <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+ <skip />
<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>
@@ -581,10 +583,8 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"Vymazať všetko"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Spravovať"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"História"</string>
- <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
- <skip />
- <!-- no translation found for notification_history_button_description (1578657591405033383) -->
- <skip />
+ <string name="notification_settings_button_description" msgid="2441994740884163889">"Nastavenia upozornení"</string>
+ <string name="notification_history_button_description" msgid="1578657591405033383">"História upozornení"</string>
<string name="notification_section_header_incoming" msgid="850925217908095197">"Nové"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"Tichý"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Upozornenia"</string>
@@ -1413,9 +1413,15 @@
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Aktuálna aplikácia"</string>
<string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Dostupnosť"</string>
<string name="shortcut_helper_title" msgid="8567500639300970049">"Klávesové skratky"</string>
+ <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <skip />
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Vyhľadávacie odkazy"</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>
+ <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <skip />
<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>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Presúvadlo"</string>
diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml
index 4ca7b244e9a7..15f1b3ccca7d 100644
--- a/packages/SystemUI/res/values-sl/strings.xml
+++ b/packages/SystemUI/res/values-sl/strings.xml
@@ -112,6 +112,8 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Želite posneti zaslon?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Snemanje ene aplikacije"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Snemanje celotnega zaslona"</string>
+ <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+ <skip />
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Pri snemanju celotnega zaslona se posname vse, kar je prikazano na zaslonu. Zato bodite previdni z gesli, podatki za plačilo, sporočili, fotografijami ter z zvokom in videom."</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Pri snemanju aplikacije se posname vse, kar je prikazano ali predvajano v tej aplikaciji. Zato bodite previdni z gesli, podatki za plačilo, sporočili, fotografijami ter z zvokom in videom."</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Snemanje zaslona"</string>
@@ -1413,9 +1415,15 @@
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Trenutna aplikacija"</string>
<string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Dostopnost"</string>
<string name="shortcut_helper_title" msgid="8567500639300970049">"Bližnjične tipke"</string>
+ <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <skip />
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Bližnjice za iskanje"</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>
+ <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <skip />
<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>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Ročica za vlečenje"</string>
diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml
index ddea921e900d..7f74487ac2b0 100644
--- a/packages/SystemUI/res/values-sq/strings.xml
+++ b/packages/SystemUI/res/values-sq/strings.xml
@@ -112,6 +112,8 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Të regjistrohet ekrani?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Regjistro një aplikacion"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Regjistro të gjithë ekranin"</string>
+ <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+ <skip />
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Kur regjistron të gjithë ekranin, regjistrohet çdo gjë e shfaqur në ekranin tënd. Prandaj, ki kujdes me gjërat si fjalëkalimet, detajet e pagesave, mesazhet, fotografitë, si dhe audion dhe videon."</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Kur regjistron një aplikacion, regjistrohet çdo gjë që shfaqet ose luhet në atë aplikacion. Prandaj, ki kujdes me gjërat si fjalëkalimet, detajet e pagesave, mesazhet, fotografitë, si dhe audion dhe videon."</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Regjistro ekranin"</string>
@@ -1413,9 +1415,15 @@
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Aplikacioni aktual"</string>
<string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Qasshmëria"</string>
<string name="shortcut_helper_title" msgid="8567500639300970049">"Shkurtoret e tastierës"</string>
+ <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <skip />
<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>
+ <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <skip />
<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>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Doreza e zvarritjes"</string>
diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml
index 5936cd780e95..a1952d428f10 100644
--- a/packages/SystemUI/res/values-sr/strings.xml
+++ b/packages/SystemUI/res/values-sr/strings.xml
@@ -112,6 +112,8 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Желите да снимите екран?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Сними једну апликацију"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Сними цео екран"</string>
+ <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+ <skip />
<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>
@@ -581,10 +583,8 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"Обриши све"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Управљај"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Историја"</string>
- <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
- <skip />
- <!-- no translation found for notification_history_button_description (1578657591405033383) -->
- <skip />
+ <string name="notification_settings_button_description" msgid="2441994740884163889">"Подешавања обавештења"</string>
+ <string name="notification_history_button_description" msgid="1578657591405033383">"Историја обавештења"</string>
<string name="notification_section_header_incoming" msgid="850925217908095197">"Ново"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"Нечујно"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Обавештења"</string>
@@ -1413,9 +1413,15 @@
<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>
+ <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <skip />
<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>
+ <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <skip />
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Икона за проширивање"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"или"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Маркер за превлачење"</string>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index 45e150a4574b..b898eafc7f03 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -112,6 +112,8 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Vill du spela in det som visas på skärmen?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Spela in en app"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Spela in hela skärmen"</string>
+ <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+ <skip />
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"När du spelar in hela skärmen spelas allt som visas på skärmen in. Var försiktig med sådant som lösenord, betalningsuppgifter, meddelanden, foton, ljud och video."</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"När du spelar in en app spelas allt som visas eller spelas upp i appen in. Var försiktig med sådant som lösenord, betalningsuppgifter, meddelanden, foton, ljud och video."</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Spela in skärmen"</string>
@@ -1413,9 +1415,15 @@
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Aktuell app"</string>
<string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Tillgänglighet"</string>
<string name="shortcut_helper_title" msgid="8567500639300970049">"Kortkommandon"</string>
+ <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <skip />
<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>
+ <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <skip />
<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>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Handtag"</string>
diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
index 86ea7e492007..8731337c88f0 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -112,6 +112,8 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Ungependa kurekodi skrini yako?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Rekodi programu moja"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Rekodi skrini nzima"</string>
+ <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+ <skip />
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Unaporekodi skrini yako nzima, chochote kinachoonyeshwa kwenye skrini yako kitarekodiwa. Kwa hivyo kuwa mwangalifu na vitu kama vile manenosiri, maelezo ya malipo, ujumbe, picha, sauti na video."</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Unaporekodi programu, chochote kinachoonyeshwa au kuchezwa kwenye programu hiyo kitarekodiwa. Kwa hivyo kuwa mwangalifu na vitu kama vile manenosiri, maelezo ya malipo, ujumbe, picha, sauti na video."</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Rekodi skrini"</string>
@@ -1413,9 +1415,15 @@
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Programu Inayotumika Sasa"</string>
<string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Ufikivu"</string>
<string name="shortcut_helper_title" msgid="8567500639300970049">"Mikato ya kibodi"</string>
+ <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <skip />
<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>
+ <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <skip />
<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>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Aikoni ya buruta"</string>
diff --git a/packages/SystemUI/res/values-sw600dp/dimens.xml b/packages/SystemUI/res/values-sw600dp/dimens.xml
index 4007bb1fcb98..393631e3364b 100644
--- a/packages/SystemUI/res/values-sw600dp/dimens.xml
+++ b/packages/SystemUI/res/values-sw600dp/dimens.xml
@@ -76,6 +76,14 @@
<dimen name="large_dialog_width">472dp</dimen>
<dimen name="large_screen_shade_header_height">42dp</dimen>
+
+ <!--
+ The horizontal distance between the shade overlay panel (both notifications and quick settings)
+ and the edge of the screen. On Compact screens in portrait orientation (< w600dp) this is
+ ignored in the shade layout, which takes up the full screen width without margins.
+ -->
+ <dimen name="shade_panel_margin_horizontal">24dp</dimen>
+
<!-- start padding is smaller to account for status icon margins coming from drawable itself -->
<dimen name="hover_system_icons_container_padding_start">3dp</dimen>
<dimen name="hover_system_icons_container_padding_end">4dp</dimen>
diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml
index 93b18646a831..b75a2186ed64 100644
--- a/packages/SystemUI/res/values-ta/strings.xml
+++ b/packages/SystemUI/res/values-ta/strings.xml
@@ -112,6 +112,8 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"உங்கள் திரையை ரெக்கார்டு செய்யவா?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"ஓர் ஆப்ஸை ரெக்கார்டு செய்தல்"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"முழுத் திரையை ரெக்கார்டு செய்தல்"</string>
+ <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+ <skip />
<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>
@@ -1413,9 +1415,15 @@
<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>
+ <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <skip />
<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>
+ <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <skip />
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"விரிவாக்குவதற்கான ஐகான்"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"அல்லது"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"இழுப்பதற்கான ஹேண்டில்"</string>
diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml
index 4a976513a8da..d66c6560fd7c 100644
--- a/packages/SystemUI/res/values-te/strings.xml
+++ b/packages/SystemUI/res/values-te/strings.xml
@@ -112,6 +112,8 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"మీ స్క్రీన్‌ను రికార్డ్ చేయాలా?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"ఒక యాప్‌ను రికార్డ్ చేయండి"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"ఫుల్ స్క్రీన్‌ను రికార్డ్ చేయండి"</string>
+ <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+ <skip />
<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>
@@ -581,10 +583,8 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"అన్నీ క్లియర్ చేయండి"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"మేనేజ్ చేయండి"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"హిస్టరీ"</string>
- <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
- <skip />
- <!-- no translation found for notification_history_button_description (1578657591405033383) -->
- <skip />
+ <string name="notification_settings_button_description" msgid="2441994740884163889">"నోటిఫికేషన్ సెట్టింగ్‌లు"</string>
+ <string name="notification_history_button_description" msgid="1578657591405033383">"నోటిఫికేషన్ హిస్టరీ"</string>
<string name="notification_section_header_incoming" msgid="850925217908095197">"కొత్తవి"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"నిశ్శబ్దం"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"నోటిఫికేషన్‌లు"</string>
@@ -1413,9 +1413,15 @@
<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>
+ <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <skip />
<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>
+ <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <skip />
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"విస్తరించండి చిహ్నం"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"లేదా"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"లాగే హ్యాండిల్"</string>
diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index ac027c753d12..b7d1a32fdeba 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -112,6 +112,8 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"บันทึกหน้าจอไหม"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"บันทึกแอปเดียว"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"บันทึกทั้งหน้าจอ"</string>
+ <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+ <skip />
<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>
@@ -1413,9 +1415,15 @@
<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>
+ <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <skip />
<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>
+ <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <skip />
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"ไอคอนขยาย"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"หรือ"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"แฮนเดิลการลาก"</string>
diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index 09a0e0400310..a30a2f2f45f6 100644
--- a/packages/SystemUI/res/values-tl/strings.xml
+++ b/packages/SystemUI/res/values-tl/strings.xml
@@ -112,6 +112,8 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"I-record ang iyong screen?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Mag-record ng isang app"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"I-record ang buong screen"</string>
+ <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+ <skip />
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Kapag nire-record mo ang iyong buong screen, nire-record ang anumang ipinapakita sa screen mo. Kaya mag-ingat sa mga bagay-bagay tulad ng mga password, detalye ng pagbabayad, mensahe, larawan, at audio at video."</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Kapag nagre-record ka ng app, nire-record ang anumang ipinapakita o pine-play sa app na iyon. Kaya mag-ingat sa mga bagay-bagay tulad ng mga password, detalye ng pagbabayad, mensahe, larawan, at audio at video."</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"I-record ang screen"</string>
@@ -1413,9 +1415,15 @@
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Kasalukuyang App"</string>
<string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Accessibility"</string>
<string name="shortcut_helper_title" msgid="8567500639300970049">"Mga keyboard shortcut"</string>
+ <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <skip />
<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>
+ <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <skip />
<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>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Handle sa pag-drag"</string>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index ae9cd186520e..c79dfcf8f162 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -112,6 +112,8 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Ekranınız kaydedilsin mi?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Bir uygulamayı kaydet"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Tüm ekranı kaydedin"</string>
+ <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+ <skip />
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Tüm ekranınızı kaydettiğinizde ekranınızda gösterilen her şey kaydedilir. Bu nedenle şifre, ödeme ayrıntıları, mesaj, fotoğraf, ses ve video gibi öğeler konusunda dikkatli olun."</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Bir uygulamayı kaydettiğinizde o uygulamada gösterilen veya oynatılan her şey kaydedilir. Bu nedenle şifre, ödeme ayrıntıları, mesaj, fotoğraf, ses ve video gibi öğeler konusunda dikkatli olun."</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Ekranı kaydet"</string>
@@ -1413,9 +1415,15 @@
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Mevcut Uygulama"</string>
<string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Erişilebilirlik"</string>
<string name="shortcut_helper_title" msgid="8567500639300970049">"Klavye kısayolları"</string>
+ <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <skip />
<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>
+ <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <skip />
<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>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Sürükleme tutamacı"</string>
diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml
index 820d072f152e..2808922e166c 100644
--- a/packages/SystemUI/res/values-uk/strings.xml
+++ b/packages/SystemUI/res/values-uk/strings.xml
@@ -112,6 +112,8 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Записати відео з екрана?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Записувати один додаток"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Записувати весь екран"</string>
+ <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+ <skip />
<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>
@@ -1413,15 +1415,21 @@
<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>
+ <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <skip />
<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>
+ <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <skip />
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Значок розгортання"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"або"</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="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Навігація за допомогою клавіатури"</string>
- <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Комбінації клавіш: докладніше"</string>
+ <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Дізнайтеся більше про комбінації клавіш"</string>
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Навігація за допомогою сенсорної панелі"</string>
<string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Жести для сенсорної панелі: докладніше"</string>
<string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Навігація за допомогою клавіатури й сенсорної панелі"</string>
diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml
index b98abbda3f7b..b72464cad145 100644
--- a/packages/SystemUI/res/values-ur/strings.xml
+++ b/packages/SystemUI/res/values-ur/strings.xml
@@ -112,6 +112,8 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"آپ کی اسکرین ریکارڈ کریں؟"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"ایک ایپ ریکارڈ کریں"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"پوری اسکرین کو ریکارڈ کریں"</string>
+ <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+ <skip />
<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>
@@ -1413,9 +1415,15 @@
<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>
+ <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <skip />
<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>
+ <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <skip />
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"آئیکن پھیلائیں"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"یا"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"گھسیٹنے کا ہینڈل"</string>
diff --git a/packages/SystemUI/res/values-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml
index 3309772d5fc8..7f6275c5a1d2 100644
--- a/packages/SystemUI/res/values-uz/strings.xml
+++ b/packages/SystemUI/res/values-uz/strings.xml
@@ -112,6 +112,8 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Ekran yozib olinsinmi?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Bitta ilovani yozib olish"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Butun ekranni yozib olish"</string>
+ <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+ <skip />
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Butun ekranni yozib olishda ekranda koʻrsatilgan barcha axborotlar yozib olinadi. Shu sababli parollar, toʻlov tafsilotlari, xabarlar, suratlar, audio va video chiqmasligi uchun ehtiyot boʻling."</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Ilovani yozib olishda ilova koʻrsatilgan yoki ijro etilgan barcha axborotlar yozib olinadi. Shu sababli parollar, toʻlov tafsilotlari, xabarlar, suratlar, audio va video chiqmasligi uchun ehtiyot boʻling."</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Ekranni yozib olish"</string>
@@ -1413,9 +1415,15 @@
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Joriy ilova"</string>
<string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Qulayliklar"</string>
<string name="shortcut_helper_title" msgid="8567500639300970049">"Tezkor tugmalar"</string>
+ <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <skip />
<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>
+ <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <skip />
<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>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Surish dastagi"</string>
diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index f404a2bf455b..388ebb804828 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -112,6 +112,8 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Ghi màn hình?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Ghi một ứng dụng"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Ghi toàn màn hình"</string>
+ <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+ <skip />
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Khi bạn ghi toàn màn hình, mọi nội dung trên màn hình của bạn đều được ghi. Vì vậy, hãy thận trọng để không làm lộ thông tin như mật khẩu, thông tin thanh toán, tin nhắn, ảnh, âm thanh và video."</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Khi bạn ghi một ứng dụng, mọi nội dung xuất hiện hoặc phát trong ứng dụng đó sẽ đều được ghi. Vì vậy, hãy thận trọng để không làm lộ thông tin như mật khẩu, thông tin thanh toán, tin nhắn, ảnh, âm thanh và video."</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Ghi màn hình"</string>
@@ -581,10 +583,8 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"Xóa tất cả"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Quản lý"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Lịch sử"</string>
- <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
- <skip />
- <!-- no translation found for notification_history_button_description (1578657591405033383) -->
- <skip />
+ <string name="notification_settings_button_description" msgid="2441994740884163889">"Cài đặt thông báo"</string>
+ <string name="notification_history_button_description" msgid="1578657591405033383">"Nhật ký thông báo"</string>
<string name="notification_section_header_incoming" msgid="850925217908095197">"Mới"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"Im lặng"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Thông báo"</string>
@@ -1413,9 +1413,15 @@
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Ứng dụng hiện tại"</string>
<string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Hỗ trợ tiếp cận"</string>
<string name="shortcut_helper_title" msgid="8567500639300970049">"Phím tắt"</string>
+ <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <skip />
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Lối tắt tìm kiếm"</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>
+ <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <skip />
<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>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Nút kéo"</string>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index 24c9f40f05a7..b463900a331d 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -112,6 +112,8 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"要录制屏幕吗?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"录制单个应用"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"录制整个屏幕"</string>
+ <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+ <skip />
<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>
@@ -1413,9 +1415,15 @@
<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>
+ <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <skip />
<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>
+ <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <skip />
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"展开图标"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"或"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"拖动手柄"</string>
diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml
index e7ebe5ccf697..91185912af9d 100644
--- a/packages/SystemUI/res/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/strings.xml
@@ -112,6 +112,8 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"要錄影螢幕畫面嗎?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"錄影一個應用程式"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"錄影整個螢幕畫面"</string>
+ <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+ <skip />
<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>
@@ -1413,9 +1415,15 @@
<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>
+ <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <skip />
<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>
+ <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <skip />
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"展開圖示"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"或"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"拖曳控點"</string>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index 0f298dc314c6..2189cbfdf278 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -112,6 +112,8 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"要錄製畫面嗎?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"錄製單一應用程式"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"錄製整個畫面"</string>
+ <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+ <skip />
<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>
@@ -1413,9 +1415,15 @@
<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>
+ <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <skip />
<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>
+ <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <skip />
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"展開圖示"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"或"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"拖曳控點"</string>
diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml
index 444ca041f31b..05b5040faf85 100644
--- a/packages/SystemUI/res/values-zu/strings.xml
+++ b/packages/SystemUI/res/values-zu/strings.xml
@@ -112,6 +112,8 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Rekhoda isikrini sakho?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Rekhoda i-app eyodwa"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Rekhoda sonke isikrini"</string>
+ <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+ <skip />
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Uma urekhoda sonke isikrini sakho, noma yini evela esikrinini iyarekhodwa. Ngakho-ke qaphela ngezinto ezifana namaphasiwedi, imininingwane yenkokhelo, imilayezo, izithombe, nomsindo nevidiyo."</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Uma urekhoda i-app, noma yini evezwa noma edlala kuleyo app iyarekhodwa. Ngakho-ke qaphela ngezinto ezifana namaphasiwedi, imininingwane yenkokhelo, imilayezo, izithombe, nomsindo nevidiyo."</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Rekhoda isikrini"</string>
@@ -1413,9 +1415,15 @@
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"I-App yamanje"</string>
<string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Ukufinyeleleka"</string>
<string name="shortcut_helper_title" msgid="8567500639300970049">"Izinqamuleli zekhibhodi"</string>
+ <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <skip />
<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>
+ <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <skip />
<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>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Hudula isibambi"</string>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index de547add1d1a..c5e205c8aa40 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -584,6 +584,12 @@
-->
<dimen name="shade_panel_width">412dp</dimen>
+ <!--
+ The horizontal distance between the shade overlay panel (both notifications and quick settings)
+ and the edge of the screen. This is zero only on Compact screens (< sw600dp).
+ -->
+ <dimen name="shade_panel_margin_horizontal">0dp</dimen>
+
<dimen name="brightness_mirror_height">48dp</dimen>
<dimen name="volume_dialog_panel_transparent_padding_right">8dp</dimen>
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
index 959ab64f123b..12b5fc01845c 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
@@ -33,7 +33,6 @@ import android.graphics.PixelFormat;
import android.hardware.biometrics.BiometricAuthenticator.Modality;
import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.BiometricManager.Authenticators;
-import android.hardware.biometrics.Flags;
import android.hardware.biometrics.PromptInfo;
import android.hardware.face.FaceSensorPropertiesInternal;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
@@ -676,10 +675,8 @@ public class AuthContainerView extends LinearLayout
final Runnable endActionRunnable = () -> {
setVisibility(View.INVISIBLE);
- if (Flags.customBiometricPrompt()) {
// TODO(b/288175645): resetPrompt calls should be lifecycle aware
mPromptSelectorInteractorProvider.get().resetPrompt(getRequestId());
- }
removeWindowIfAttached();
};
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractor.kt b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractor.kt
index f1df5cabbb16..008fb26424e2 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractor.kt
@@ -16,7 +16,6 @@
package com.android.systemui.biometrics.domain.interactor
-import android.hardware.biometrics.Flags
import android.hardware.biometrics.PromptInfo
import com.android.internal.widget.LockPatternUtils
import com.android.systemui.biometrics.Utils
@@ -189,8 +188,7 @@ constructor(
val effectiveUserId = credentialInteractor.getCredentialOwnerOrSelfId(userId)
val hasCredentialViewShown = promptKind.value.isCredential()
val showBpForCredential =
- Flags.customBiometricPrompt() &&
- !Utils.isBiometricAllowed(promptInfo) &&
+ !Utils.isBiometricAllowed(promptInfo) &&
isDeviceCredentialAllowed(promptInfo) &&
promptInfo.contentView != null &&
!promptInfo.isContentViewMoreOptionsButtonUsed
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt
index 2df5f16698b4..db4b0f2522ed 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt
@@ -22,7 +22,6 @@ import android.content.Context
import android.hardware.biometrics.BiometricAuthenticator
import android.hardware.biometrics.BiometricConstants
import android.hardware.biometrics.BiometricPrompt
-import android.hardware.biometrics.Flags
import android.hardware.face.FaceManager
import android.util.Log
import android.view.MotionEvent
@@ -93,7 +92,7 @@ object BiometricViewBinder {
val attributes =
view.context.obtainStyledAttributes(
R.style.TextAppearance_AuthCredential_Indicator,
- intArrayOf(android.R.attr.textColor)
+ intArrayOf(android.R.attr.textColor),
)
val textColorHint = attributes.getColor(0, 0)
attributes.recycle()
@@ -130,13 +129,13 @@ object BiometricViewBinder {
object : AccessibilityDelegateCompat() {
override fun onInitializeAccessibilityNodeInfo(
host: View,
- info: AccessibilityNodeInfoCompat
+ info: AccessibilityNodeInfoCompat,
) {
super.onInitializeAccessibilityNodeInfo(host, info)
info.addAction(
AccessibilityActionCompat(
AccessibilityNodeInfoCompat.ACTION_CLICK,
- view.context.getString(R.string.biometric_dialog_cancel_authentication)
+ view.context.getString(R.string.biometric_dialog_cancel_authentication),
)
)
}
@@ -189,13 +188,11 @@ object BiometricViewBinder {
subtitleView.text = viewModel.subtitle.first()
descriptionView.text = viewModel.description.first()
- if (Flags.customBiometricPrompt()) {
- BiometricCustomizedViewBinder.bind(
- customizedViewContainer,
- viewModel.contentView.first(),
- legacyCallback
- )
- }
+ BiometricCustomizedViewBinder.bind(
+ customizedViewContainer,
+ viewModel.contentView.first(),
+ legacyCallback,
+ )
// set button listeners
negativeButton.setOnClickListener { legacyCallback.onButtonNegative() }
@@ -233,10 +230,7 @@ object BiometricViewBinder {
lifecycleScope.launch {
viewModel.hideSensorIcon.collect { showWithoutIcon ->
if (!showWithoutIcon) {
- PromptIconViewBinder.bind(
- iconView,
- viewModel,
- )
+ PromptIconViewBinder.bind(iconView, viewModel)
}
}
}
@@ -421,7 +415,7 @@ object BiometricViewBinder {
launch {
viewModel.onAnnounceAccessibilityHint(
event,
- accessibilityManager.isTouchExplorationEnabled
+ accessibilityManager.isTouchExplorationEnabled,
)
}
false
@@ -444,10 +438,7 @@ object BiometricViewBinder {
haptics.flag,
)
} else {
- vibratorHelper.performHapticFeedback(
- view,
- haptics.constant,
- )
+ vibratorHelper.performHapticFeedback(view, haptics.constant)
}
}
is PromptViewModel.HapticsToPlay.MSDL -> {
@@ -561,14 +552,12 @@ class Spaghetti(
viewModel.showAuthenticated(
modality = authenticatedModality,
dismissAfterDelay = 500,
- helpMessage = if (msgId != null) applicationContext.getString(msgId) else ""
+ helpMessage = if (msgId != null) applicationContext.getString(msgId) else "",
)
}
}
- private fun getHelpForSuccessfulAuthentication(
- authenticatedModality: BiometricModality,
- ): Int? {
+ private fun getHelpForSuccessfulAuthentication(authenticatedModality: BiometricModality): Int? {
// for coex, show a message when face succeeds after fingerprint has also started
if (authenticatedModality != BiometricModality.Face) {
return null
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/CredentialViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/CredentialViewBinder.kt
index 3ea91f05827c..39543e78f784 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/CredentialViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/CredentialViewBinder.kt
@@ -1,6 +1,5 @@
package com.android.systemui.biometrics.ui.binder
-import android.hardware.biometrics.Flags
import android.view.View
import android.view.ViewGroup
import android.widget.Button
@@ -67,7 +66,7 @@ object CredentialViewBinder {
updateForContentDimensions(
containerWidth,
containerHeight,
- 0 // animateDurationMs
+ 0, // animateDurationMs
)
}
}
@@ -81,13 +80,11 @@ object CredentialViewBinder {
subtitleView.textOrHide = header.subtitle
descriptionView.textOrHide = header.description
- if (Flags.customBiometricPrompt()) {
- BiometricCustomizedViewBinder.bind(
- customizedViewContainer,
- header.contentView,
- legacyCallback
- )
- }
+ BiometricCustomizedViewBinder.bind(
+ customizedViewContainer,
+ header.contentView,
+ legacyCallback,
+ )
iconView?.setImageDrawable(header.icon)
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/CredentialViewModel.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/CredentialViewModel.kt
index 761c3da77a4d..0c5c723a4b2f 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/CredentialViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/CredentialViewModel.kt
@@ -2,7 +2,6 @@ package com.android.systemui.biometrics.ui.viewmodel
import android.content.Context
import android.graphics.drawable.Drawable
-import android.hardware.biometrics.Flags.customBiometricPrompt
import android.hardware.biometrics.PromptContentView
import android.text.InputType
import com.android.internal.widget.LockPatternView
@@ -36,21 +35,17 @@ constructor(
val header: Flow<CredentialHeaderViewModel> =
combine(
credentialInteractor.prompt.filterIsInstance<BiometricPromptRequest.Credential>(),
- credentialInteractor.showTitleOnly
+ credentialInteractor.showTitleOnly,
) { request, showTitleOnly ->
- val flagEnabled = customBiometricPrompt()
- val showTitleOnlyForCredential = showTitleOnly && flagEnabled
BiometricPromptHeaderViewModelImpl(
request,
user = request.userInfo,
title = request.title,
- subtitle = if (showTitleOnlyForCredential) "" else request.subtitle,
- contentView =
- if (flagEnabled && !showTitleOnlyForCredential) request.contentView else null,
- description =
- if (flagEnabled && request.contentView != null) "" else request.description,
+ subtitle = if (showTitleOnly) "" else request.subtitle,
+ contentView = if (!showTitleOnly) request.contentView else null,
+ description = if (request.contentView != null) "" else request.description,
icon = applicationContext.asLockIcon(request.userInfo.deviceCredentialOwnerId),
- showEmergencyCallButton = request.showEmergencyCallButton
+ showEmergencyCallButton = request.showEmergencyCallButton,
)
}
@@ -125,7 +120,7 @@ constructor(
/** Check a pattern and update [validatedAttestation] or [remainingAttempts]. */
suspend fun checkCredential(
pattern: List<LockPatternView.Cell>,
- header: CredentialHeaderViewModel
+ header: CredentialHeaderViewModel,
) = checkCredential(credentialInteractor.checkCredential(header.asRequest(), pattern = pattern))
private suspend fun checkCredential(result: CredentialStatus) {
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 0ac9405d0d8c..cbf783d66d42 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
@@ -27,7 +27,6 @@ import android.graphics.drawable.BitmapDrawable
import android.graphics.drawable.Drawable
import android.hardware.biometrics.BiometricFingerprintConstants
import android.hardware.biometrics.BiometricPrompt
-import android.hardware.biometrics.Flags.customBiometricPrompt
import android.hardware.biometrics.PromptContentView
import android.os.UserHandle
import android.text.TextPaint
@@ -145,13 +144,13 @@ constructor(
rotatedBounds,
params.naturalDisplayWidth,
params.naturalDisplayHeight,
- rotation.ordinal
+ rotation.ordinal,
)
Rect(
rotatedBounds.left,
rotatedBounds.top,
params.logicalDisplayWidth - rotatedBounds.right,
- params.logicalDisplayHeight - rotatedBounds.bottom
+ params.logicalDisplayHeight - rotatedBounds.bottom,
)
}
.distinctUntilChanged()
@@ -263,7 +262,7 @@ constructor(
promptKind,
displayStateInteractor.isLargeScreen,
displayStateInteractor.currentRotation,
- modalities
+ modalities,
) { forceLarge, promptKind, isLargeScreen, rotation, modalities ->
when {
forceLarge ||
@@ -351,7 +350,7 @@ constructor(
0,
0,
landscapeMediumHorizontalPadding,
- landscapeMediumBottomPadding
+ landscapeMediumBottomPadding,
)
}
PromptPosition.Left ->
@@ -365,7 +364,7 @@ constructor(
landscapeMediumHorizontalPadding,
0,
0,
- landscapeMediumBottomPadding
+ landscapeMediumBottomPadding,
)
}
PromptPosition.Top ->
@@ -474,7 +473,7 @@ constructor(
promptSelectorInteractor.prompt
.map {
when {
- !(customBiometricPrompt()) || it == null -> Pair(null, "")
+ it == null -> Pair(null, "")
else -> context.getUserBadgedLogoInfo(it, iconProvider, activityTaskManager)
}
}
@@ -490,9 +489,7 @@ constructor(
/** Custom content view for the prompt. */
val contentView: Flow<PromptContentView?> =
- promptSelectorInteractor.prompt
- .map { if (customBiometricPrompt()) it?.contentView else null }
- .distinctUntilChanged()
+ promptSelectorInteractor.prompt.map { it?.contentView }.distinctUntilChanged()
private val originalDescription =
promptSelectorInteractor.prompt.map { it?.description ?: "" }.distinctUntilChanged()
@@ -521,7 +518,7 @@ constructor(
val attributes =
context.obtainStyledAttributes(
R.style.TextAppearance_AuthCredential_Title,
- intArrayOf(android.R.attr.textSize)
+ intArrayOf(android.R.attr.textSize),
)
val paint = TextPaint()
paint.textSize = attributes.getDimensionPixelSize(0, 0).toFloat()
@@ -566,7 +563,7 @@ constructor(
private fun getHorizontalPadding(
size: PromptSize,
modalities: BiometricModalities,
- hasOnlyOneLineTitle: Boolean
+ hasOnlyOneLineTitle: Boolean,
) =
if (size.isSmall) {
-smallHorizontalGuidelinePadding
@@ -582,21 +579,13 @@ constructor(
/** If the indicator (help, error) message should be shown. */
val isIndicatorMessageVisible: Flow<Boolean> =
- combine(
- size,
- position,
- message,
- ) { size, _, message ->
+ combine(size, position, message) { size, _, message ->
size.isMedium && message.message.isNotBlank()
}
/** If the auth is pending confirmation and the confirm button should be shown. */
val isConfirmButtonVisible: Flow<Boolean> =
- combine(
- size,
- position,
- isPendingConfirmation,
- ) { size, _, isPendingConfirmation ->
+ combine(size, position, isPendingConfirmation) { size, _, isPendingConfirmation ->
size.isNotSmall && isPendingConfirmation
}
@@ -606,24 +595,22 @@ constructor(
/** If the negative button should be shown. */
val isNegativeButtonVisible: Flow<Boolean> =
- combine(
+ combine(size, position, isAuthenticated, promptSelectorInteractor.isCredentialAllowed) {
size,
- position,
- isAuthenticated,
- promptSelectorInteractor.isCredentialAllowed,
- ) { size, _, authState, credentialAllowed ->
+ _,
+ authState,
+ credentialAllowed ->
size.isNotSmall && authState.isNotAuthenticated && !credentialAllowed
}
/** If the cancel button should be shown (. */
val isCancelButtonVisible: Flow<Boolean> =
- combine(
+ combine(size, position, isAuthenticated, isNegativeButtonVisible, isConfirmButtonVisible) {
size,
- position,
- isAuthenticated,
- isNegativeButtonVisible,
- isConfirmButtonVisible,
- ) { size, _, authState, showNegativeButton, showConfirmButton ->
+ _,
+ authState,
+ showNegativeButton,
+ showConfirmButton ->
size.isNotSmall && authState.isAuthenticated && !showNegativeButton && showConfirmButton
}
@@ -633,33 +620,28 @@ constructor(
* fingerprint sensor.
*/
val canTryAgainNow: Flow<Boolean> =
- combine(
- _canTryAgainNow,
+ combine(_canTryAgainNow, size, position, isAuthenticated, isRetrySupported) {
+ readyToTryAgain,
size,
- position,
- isAuthenticated,
- isRetrySupported,
- ) { readyToTryAgain, size, _, authState, supportsRetry ->
+ _,
+ authState,
+ supportsRetry ->
readyToTryAgain && size.isNotSmall && supportsRetry && authState.isNotAuthenticated
}
/** If the try again button show be shown (only the button, see [canTryAgainNow]). */
val isTryAgainButtonVisible: Flow<Boolean> =
- combine(
- canTryAgainNow,
- modalities,
- ) { tryAgainIsPossible, modalities ->
+ combine(canTryAgainNow, modalities) { tryAgainIsPossible, modalities ->
tryAgainIsPossible && modalities.hasFaceOnly
}
/** If the credential fallback button show be shown. */
val isCredentialButtonVisible: Flow<Boolean> =
- combine(
+ combine(size, position, isAuthenticated, promptSelectorInteractor.isCredentialAllowed) {
size,
- position,
- isAuthenticated,
- promptSelectorInteractor.isCredentialAllowed,
- ) { size, _, authState, credentialAllowed ->
+ _,
+ authState,
+ credentialAllowed ->
size.isMedium && authState.isNotAuthenticated && credentialAllowed
}
@@ -759,10 +741,7 @@ constructor(
*
* Ignored if the user has already authenticated.
*/
- suspend fun showTemporaryHelp(
- message: String,
- messageAfterHelp: String = "",
- ) = coroutineScope {
+ suspend fun showTemporaryHelp(message: String, messageAfterHelp: String = "") = coroutineScope {
if (_isAuthenticated.value.isAuthenticated) {
return@coroutineScope
}
@@ -910,13 +889,13 @@ constructor(
udfpsUtils.getTouchInNativeCoordinates(
event.getPointerId(0),
event,
- udfpsOverlayParams.value
+ udfpsOverlayParams.value,
)
if (
!udfpsUtils.isWithinSensorArea(
event.getPointerId(0),
event,
- udfpsOverlayParams.value
+ udfpsOverlayParams.value,
)
) {
_accessibilityHint.emit(
@@ -925,7 +904,7 @@ constructor(
context,
scaledTouch.x,
scaledTouch.y,
- udfpsOverlayParams.value
+ udfpsOverlayParams.value,
)
)
}
@@ -948,10 +927,7 @@ constructor(
if (msdlFeedback()) {
HapticsToPlay.MSDL(MSDLToken.UNLOCK, authInteractionProperties)
} else {
- HapticsToPlay.HapticConstant(
- HapticFeedbackConstants.BIOMETRIC_CONFIRM,
- flag = null,
- )
+ HapticsToPlay.HapticConstant(HapticFeedbackConstants.BIOMETRIC_CONFIRM, flag = null)
}
_hapticsToPlay.value = haptics
}
@@ -961,10 +937,7 @@ constructor(
if (msdlFeedback()) {
HapticsToPlay.MSDL(MSDLToken.FAILURE, authInteractionProperties)
} else {
- HapticsToPlay.HapticConstant(
- HapticFeedbackConstants.BIOMETRIC_REJECT,
- flag = null,
- )
+ HapticsToPlay.HapticConstant(HapticFeedbackConstants.BIOMETRIC_REJECT, flag = null)
}
_hapticsToPlay.value = haptics
}
@@ -1006,7 +979,7 @@ constructor(
private fun Context.getUserBadgedLogoInfo(
prompt: BiometricPromptRequest.Biometric,
iconProvider: IconProvider,
- activityTaskManager: ActivityTaskManager
+ activityTaskManager: ActivityTaskManager,
): Pair<Drawable?, String> {
var icon: Drawable? =
if (prompt.logoBitmap != null) BitmapDrawable(resources, prompt.logoBitmap) else null
@@ -1045,7 +1018,7 @@ private fun Context.getUserBadgedLogoInfo(
packageManager
.getUserBadgedLabel(
packageManager.getApplicationLabel(appInfo),
- UserHandle.of(userId)
+ UserHandle.of(userId),
)
.toString()
}
@@ -1070,7 +1043,7 @@ private fun BiometricPromptRequest.Biometric.getComponentNameForLogo(
private fun BiometricPromptRequest.Biometric.getApplicationInfo(
context: Context,
- componentNameForLogo: ComponentName?
+ componentNameForLogo: ComponentName?,
): ApplicationInfo? {
val packageName =
when {
@@ -1088,7 +1061,7 @@ private fun BiometricPromptRequest.Biometric.getApplicationInfo(
try {
context.packageManager.getApplicationInfo(
packageName,
- PackageManager.MATCH_DISABLED_COMPONENTS or PackageManager.MATCH_ANY_USER
+ PackageManager.MATCH_DISABLED_COMPONENTS or PackageManager.MATCH_ANY_USER,
)
} catch (e: PackageManager.NameNotFoundException) {
Log.w(PromptViewModel.TAG, "Cannot find application info for $opPackageName", e)
diff --git a/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.kt b/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.kt
index a33e0ac0b33a..44dd34a89303 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.kt
@@ -32,6 +32,8 @@ import com.android.systemui.communal.domain.interactor.CommunalSceneTransitionIn
import com.android.systemui.communal.shared.log.CommunalMetricsLogger
import com.android.systemui.communal.shared.log.CommunalStatsLogProxyImpl
import com.android.systemui.communal.shared.model.CommunalScenes
+import com.android.systemui.communal.shared.model.GlanceableHubMultiUserHelper
+import com.android.systemui.communal.shared.model.GlanceableHubMultiUserHelperImpl
import com.android.systemui.communal.util.CommunalColors
import com.android.systemui.communal.util.CommunalColorsImpl
import com.android.systemui.communal.widgets.CommunalWidgetModule
@@ -65,6 +67,7 @@ import kotlinx.coroutines.CoroutineScope
CommunalSettingsRepositoryModule::class,
CommunalSmartspaceRepositoryModule::class,
CommunalStartableModule::class,
+ GlanceableHubWidgetManagerModule::class,
]
)
interface CommunalModule {
@@ -91,6 +94,11 @@ interface CommunalModule {
impl: CommunalSceneTransitionInteractor
): CoreStartable
+ @Binds
+ fun bindGlanceableHubMultiUserHelper(
+ impl: GlanceableHubMultiUserHelperImpl
+ ): GlanceableHubMultiUserHelper
+
companion object {
const val LOGGABLE_PREFIXES = "loggable_prefixes"
const val LAUNCHER_PACKAGE = "launcher_package"
@@ -106,19 +114,14 @@ interface CommunalModule {
sceneKeys = listOf(CommunalScenes.Blank, CommunalScenes.Communal),
initialSceneKey = CommunalScenes.Blank,
navigationDistances =
- mapOf(
- CommunalScenes.Blank to 0,
- CommunalScenes.Communal to 1,
- ),
+ mapOf(CommunalScenes.Blank to 0, CommunalScenes.Communal to 1),
)
return SceneDataSourceDelegator(applicationScope, config)
}
@Provides
@SysUISingleton
- fun providesCommunalBackupUtils(
- @Application context: Context,
- ): CommunalBackupUtils {
+ fun providesCommunalBackupUtils(@Application context: Context): CommunalBackupUtils {
return CommunalBackupUtils(context)
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalStartableModule.kt b/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalStartableModule.kt
index 6cbf5405f46b..2d19b026489c 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalStartableModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalStartableModule.kt
@@ -24,6 +24,7 @@ import com.android.systemui.communal.CommunalOngoingContentStartable
import com.android.systemui.communal.CommunalSceneStartable
import com.android.systemui.communal.log.CommunalLoggerStartable
import com.android.systemui.communal.widgets.CommunalAppWidgetHostStartable
+import com.android.systemui.dagger.qualifiers.PerUser
import dagger.Binds
import dagger.Module
import dagger.multibindings.ClassKey
@@ -48,6 +49,7 @@ interface CommunalStartableModule {
@Binds
@IntoMap
+ @PerUser
@ClassKey(CommunalAppWidgetHostStartable::class)
fun bindCommunalAppWidgetHostStartable(impl: CommunalAppWidgetHostStartable): CoreStartable
diff --git a/packages/SystemUI/src/com/android/systemui/communal/dagger/GlanceableHubWidgetManagerModule.kt b/packages/SystemUI/src/com/android/systemui/communal/dagger/GlanceableHubWidgetManagerModule.kt
new file mode 100644
index 000000000000..8c0758398cb1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/dagger/GlanceableHubWidgetManagerModule.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.communal.dagger
+
+import com.android.server.servicewatcher.ServiceWatcher.ServiceSupplier
+import com.android.systemui.communal.widgets.GlanceableHubWidgetManagerServiceInfo
+import com.android.systemui.communal.widgets.GlanceableHubWidgetManagerServiceSupplier
+import com.android.systemui.communal.widgets.GlanceableHubWidgetManagerServiceWatcherFactoryImpl
+import com.android.systemui.communal.widgets.ServiceWatcherFactory
+import dagger.Binds
+import dagger.Module
+
+@Module
+interface GlanceableHubWidgetManagerModule {
+ @Binds
+ fun bindServiceSupplier(
+ impl: GlanceableHubWidgetManagerServiceSupplier
+ ): ServiceSupplier<GlanceableHubWidgetManagerServiceInfo?>
+
+ @Binds
+ fun bindServiceWatcherFactory(
+ impl: GlanceableHubWidgetManagerServiceWatcherFactoryImpl
+ ): ServiceWatcherFactory<GlanceableHubWidgetManagerServiceInfo?>
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/db/CommunalDatabase.kt b/packages/SystemUI/src/com/android/systemui/communal/data/db/CommunalDatabase.kt
index 17f4f0c83d6f..e72088f37fa7 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/data/db/CommunalDatabase.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/db/CommunalDatabase.kt
@@ -17,6 +17,7 @@
package com.android.systemui.communal.data.db
import android.content.Context
+import android.os.Process
import android.util.Log
import androidx.annotation.VisibleForTesting
import androidx.room.Database
@@ -24,6 +25,7 @@ import androidx.room.Room
import androidx.room.RoomDatabase
import androidx.room.migration.Migration
import androidx.sqlite.db.SupportSQLiteDatabase
+import com.android.systemui.communal.shared.model.GlanceableHubMultiUserHelperImpl
import com.android.systemui.res.R
@Database(entities = [CommunalWidgetItem::class, CommunalItemRank::class], version = 4)
@@ -44,6 +46,11 @@ abstract class CommunalDatabase : RoomDatabase() {
* new instance is created.
*/
fun getInstance(context: Context, callback: Callback? = null): CommunalDatabase {
+ with(GlanceableHubMultiUserHelperImpl(Process.myUserHandle())) {
+ // Assert that the database is never accessed from a headless system user.
+ assertNotInHeadlessSystemUser()
+ }
+
if (instance == null) {
instance =
Room.databaseBuilder(
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/db/CommunalWidgetDao.kt b/packages/SystemUI/src/com/android/systemui/communal/data/db/CommunalWidgetDao.kt
index 258480e7fef5..3d40aa75b488 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/data/db/CommunalWidgetDao.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/db/CommunalWidgetDao.kt
@@ -25,6 +25,7 @@ import androidx.room.RoomDatabase
import androidx.room.Transaction
import androidx.room.Update
import androidx.sqlite.db.SupportSQLiteDatabase
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.systemui.communal.nano.CommunalHubState
import com.android.systemui.communal.shared.model.CommunalContentSize
import com.android.systemui.communal.widgets.CommunalWidgetHost
@@ -39,7 +40,6 @@ import javax.inject.Named
import javax.inject.Provider
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
-import com.android.app.tracing.coroutines.launchTraced as launch
/**
* Callback that will be invoked when the Room database is created. Then the database will be
@@ -224,9 +224,9 @@ interface CommunalWidgetDao {
): Long {
val widgets = getWidgetsNow()
- // If rank is not specified, rank it last by finding the current maximum rank and increment
- // by 1. If the new widget is the first widget, set the rank to 0.
- val newRank = rank ?: widgets.keys.maxOfOrNull { it.rank + 1 } ?: 0
+ // If rank is not specified (null or less than 0), rank it last by finding the current
+ // maximum rank and increment by 1. If the new widget is the first widget, set rank to 0.
+ val newRank = rank?.takeIf { it >= 0 } ?: widgets.keys.maxOfOrNull { it.rank + 1 } ?: 0
// Shift widgets after [rank], unless widget is added at the end.
if (rank != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepository.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepository.kt
index e164587ce02d..29569f8b7df5 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepository.kt
@@ -21,6 +21,7 @@ import android.appwidget.AppWidgetProviderInfo
import android.content.ComponentName
import android.os.UserHandle
import android.os.UserManager
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.systemui.Flags.communalWidgetResizing
import com.android.systemui.common.data.repository.PackageChangeRepository
import com.android.systemui.common.shared.model.PackageInstallSession
@@ -51,11 +52,10 @@ import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
-import com.android.app.tracing.coroutines.launchTraced as launch
/** Encapsulates the state of widgets for communal mode. */
interface CommunalWidgetRepository {
- /** A flow of information about active communal widgets stored in database. */
+ /** A flow of the list of Glanceable Hub widgets ordered by rank. */
val communalWidgets: Flow<List<CommunalWidgetContentModel>>
/**
@@ -106,8 +106,12 @@ interface CommunalWidgetRepository {
fun resizeWidget(appWidgetId: Int, spanY: Int, widgetIdToRankMap: Map<Int, Int>)
}
+/**
+ * The local implementation of the [CommunalWidgetRepository] that should be injected in a
+ * foreground user process.
+ */
@SysUISingleton
-class CommunalWidgetRepositoryImpl
+class CommunalWidgetRepositoryLocalImpl
@Inject
constructor(
private val appWidgetHost: CommunalAppWidgetHost,
@@ -123,7 +127,7 @@ constructor(
private val defaultWidgetPopulation: DefaultWidgetPopulation,
) : CommunalWidgetRepository {
companion object {
- const val TAG = "CommunalWidgetRepository"
+ const val TAG = "CommunalWidgetRepositoryLocalImpl"
}
private val logger = Logger(logBuffer, TAG)
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryModule.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryModule.kt
index b502fb11d7ac..824edcb26809 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryModule.kt
@@ -17,11 +17,24 @@
package com.android.systemui.communal.data.repository
-import dagger.Binds
+import com.android.systemui.communal.shared.model.GlanceableHubMultiUserHelper
+import dagger.Lazy
import dagger.Module
+import dagger.Provides
@Module
interface CommunalWidgetRepositoryModule {
- @Binds
- fun communalWidgetRepository(impl: CommunalWidgetRepositoryImpl): CommunalWidgetRepository
+ companion object {
+ @Provides
+ fun provideCommunalWidgetRepository(
+ localImpl: Lazy<CommunalWidgetRepositoryLocalImpl>,
+ remoteImpl: Lazy<CommunalWidgetRepositoryRemoteImpl>,
+ helper: GlanceableHubMultiUserHelper,
+ ): CommunalWidgetRepository {
+ // Provide an implementation based on the current user.
+ return if (helper.glanceableHubHsumFlagEnabled && helper.isInHeadlessSystemUser())
+ remoteImpl.get()
+ else localImpl.get()
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryRemoteImpl.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryRemoteImpl.kt
new file mode 100644
index 000000000000..6682186c94f5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryRemoteImpl.kt
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.data.repository
+
+import android.content.ComponentName
+import android.os.UserHandle
+import com.android.systemui.communal.shared.model.CommunalWidgetContentModel
+import com.android.systemui.communal.shared.model.GlanceableHubMultiUserHelper
+import com.android.systemui.communal.widgets.GlanceableHubWidgetManager
+import com.android.systemui.communal.widgets.WidgetConfigurator
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.launch
+
+/**
+ * The remote implementation of the [CommunalWidgetRepository] that should be injected in a headless
+ * system user process. This implementation receives widget data from and routes requests to the
+ * remote service in the foreground user.
+ */
+@SysUISingleton
+class CommunalWidgetRepositoryRemoteImpl
+@Inject
+constructor(
+ @Background private val bgScope: CoroutineScope,
+ private val glanceableHubWidgetManager: GlanceableHubWidgetManager,
+ glanceableHubMultiUserHelper: GlanceableHubMultiUserHelper,
+) : CommunalWidgetRepository {
+
+ init {
+ // This is the implementation for the headless system user. For the foreground user
+ // implementation see [CommunalWidgetRepositoryLocalImpl].
+ glanceableHubMultiUserHelper.assertInHeadlessSystemUser()
+ }
+
+ override val communalWidgets: Flow<List<CommunalWidgetContentModel>> =
+ glanceableHubWidgetManager.widgets
+
+ override fun addWidget(
+ provider: ComponentName,
+ user: UserHandle,
+ rank: Int?,
+ configurator: WidgetConfigurator?,
+ ) {
+ bgScope.launch { glanceableHubWidgetManager.addWidget(provider, user, rank, configurator) }
+ }
+
+ override fun deleteWidget(widgetId: Int) {
+ bgScope.launch { glanceableHubWidgetManager.deleteWidget(widgetId) }
+ }
+
+ override fun updateWidgetOrder(widgetIdToRankMap: Map<Int, Int>) {
+ bgScope.launch { glanceableHubWidgetManager.updateWidgetOrder(widgetIdToRankMap) }
+ }
+
+ override fun resizeWidget(appWidgetId: Int, spanY: Int, widgetIdToRankMap: Map<Int, Int>) {
+ bgScope.launch {
+ glanceableHubWidgetManager.resizeWidget(appWidgetId, spanY, widgetIdToRankMap)
+ }
+ }
+
+ override fun restoreWidgets(oldToNewWidgetIdMap: Map<Int, Int>) {
+ throw IllegalStateException("Restore widgets should be performed on a foreground user")
+ }
+
+ override fun abortRestoreWidgets() {
+ throw IllegalStateException("Restore widgets should be performed on a foreground user")
+ }
+}
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 dc248059b3d4..602fe307a1fe 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
@@ -40,7 +40,6 @@ import com.android.systemui.communal.shared.model.CommunalContentSize.THIRD
import com.android.systemui.communal.shared.model.CommunalScenes
import com.android.systemui.communal.shared.model.CommunalWidgetContentModel
import com.android.systemui.communal.shared.model.EditModeState
-import com.android.systemui.communal.widgets.CommunalAppWidgetHost
import com.android.systemui.communal.widgets.EditWidgetsActivityStarter
import com.android.systemui.communal.widgets.WidgetConfigurator
import com.android.systemui.dagger.SysUISingleton
@@ -108,7 +107,6 @@ constructor(
keyguardInteractor: KeyguardInteractor,
keyguardTransitionInteractor: KeyguardTransitionInteractor,
communalSettingsInteractor: CommunalSettingsInteractor,
- private val appWidgetHost: CommunalAppWidgetHost,
private val editWidgetsActivityStarter: EditWidgetsActivityStarter,
private val userTracker: UserTracker,
private val activityStarter: ActivityStarter,
@@ -451,7 +449,6 @@ constructor(
appWidgetId = widget.appWidgetId,
rank = widget.rank,
providerInfo = widget.providerInfo,
- appWidgetHost = appWidgetHost,
inQuietMode = isQuietModeEnabled(widget.providerInfo.profile),
size = CommunalContentSize.toSize(widget.spanY),
)
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 4c2c094a4aea..30f580e472e8 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
@@ -23,7 +23,6 @@ import android.content.pm.ApplicationInfo
import android.graphics.Bitmap
import android.widget.RemoteViews
import com.android.systemui.communal.shared.model.CommunalContentSize
-import com.android.systemui.communal.widgets.CommunalAppWidgetHost
import java.util.UUID
/** Encapsulates data for a communal content. */
@@ -60,7 +59,6 @@ sealed interface CommunalContentModel {
override val appWidgetId: Int,
override val rank: Int,
val providerInfo: AppWidgetProviderInfo,
- val appWidgetHost: CommunalAppWidgetHost,
val inQuietMode: Boolean,
override val size: CommunalContentSize,
) : WidgetContent {
diff --git a/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalWidgetContentModel.aidl b/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalWidgetContentModel.aidl
new file mode 100644
index 000000000000..a215698eaeba
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalWidgetContentModel.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 com.android.systemui.communal.shared.model;
+
+parcelable CommunalWidgetContentModel;
diff --git a/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalWidgetContentModel.kt b/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalWidgetContentModel.kt
index 0c9ea789f3d1..f23347d083d6 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalWidgetContentModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalWidgetContentModel.kt
@@ -19,21 +19,60 @@ package com.android.systemui.communal.shared.model
import android.appwidget.AppWidgetProviderInfo
import android.content.ComponentName
import android.graphics.Bitmap
+import android.os.Parcel
+import android.os.Parcelable
import android.os.UserHandle
/** Encapsulates data for a communal widget. */
-sealed interface CommunalWidgetContentModel {
+sealed interface CommunalWidgetContentModel : Parcelable {
val appWidgetId: Int
val rank: Int
val spanY: Int
+ // Used for distinguishing subtypes when reading from a parcel.
+ val type: Int
+
/** Widget is ready to display */
data class Available(
override val appWidgetId: Int,
val providerInfo: AppWidgetProviderInfo,
override val rank: Int,
override val spanY: Int,
- ) : CommunalWidgetContentModel
+ ) : CommunalWidgetContentModel {
+
+ override val type = TYPE_AVAILABLE
+
+ constructor(
+ parcel: Parcel
+ ) : this(
+ parcel.readInt(),
+ requireNotNull(parcel.readTypedObject(AppWidgetProviderInfo.CREATOR)),
+ parcel.readInt(),
+ parcel.readInt(),
+ )
+
+ override fun writeToParcel(parcel: Parcel, flags: Int) {
+ parcel.writeInt(type)
+ parcel.writeInt(appWidgetId)
+ parcel.writeTypedObject(providerInfo, flags)
+ parcel.writeInt(rank)
+ parcel.writeInt(spanY)
+ }
+
+ override fun describeContents(): Int {
+ return 0
+ }
+
+ companion object CREATOR : Parcelable.Creator<Available> {
+ override fun createFromParcel(parcel: Parcel): Available {
+ return Available(parcel)
+ }
+
+ override fun newArray(size: Int): Array<Available?> {
+ return arrayOfNulls(size)
+ }
+ }
+ }
/** Widget is pending installation */
data class Pending(
@@ -43,5 +82,61 @@ sealed interface CommunalWidgetContentModel {
val icon: Bitmap?,
val user: UserHandle,
override val spanY: Int,
- ) : CommunalWidgetContentModel
+ ) : CommunalWidgetContentModel {
+
+ override val type = TYPE_PENDING
+
+ constructor(
+ parcel: Parcel
+ ) : this(
+ parcel.readInt(),
+ parcel.readInt(),
+ requireNotNull(parcel.readTypedObject(ComponentName.CREATOR)),
+ parcel.readTypedObject(Bitmap.CREATOR),
+ requireNotNull(parcel.readTypedObject(UserHandle.CREATOR)),
+ parcel.readInt(),
+ )
+
+ override fun writeToParcel(parcel: Parcel, flags: Int) {
+ parcel.writeInt(type)
+ parcel.writeInt(appWidgetId)
+ parcel.writeInt(rank)
+ parcel.writeTypedObject(componentName, flags)
+ parcel.writeTypedObject(icon, flags)
+ parcel.writeTypedObject(user, flags)
+ parcel.writeInt(spanY)
+ }
+
+ override fun describeContents(): Int {
+ return 0
+ }
+
+ companion object CREATOR : Parcelable.Creator<Pending> {
+ override fun createFromParcel(parcel: Parcel): Pending {
+ return Pending(parcel)
+ }
+
+ override fun newArray(size: Int): Array<Pending?> {
+ return arrayOfNulls(size)
+ }
+ }
+ }
+
+ // Used for distinguishing subtypes when reading from a parcel.
+ companion object CREATOR : Parcelable.Creator<CommunalWidgetContentModel> {
+ private const val TYPE_AVAILABLE = 0
+ private const val TYPE_PENDING = 1
+
+ override fun createFromParcel(parcel: Parcel): CommunalWidgetContentModel {
+ return when (val type = parcel.readInt()) {
+ TYPE_AVAILABLE -> Available(parcel)
+ TYPE_PENDING -> Pending(parcel)
+ else -> throw IllegalArgumentException("Unknown type: $type")
+ }
+ }
+
+ override fun newArray(size: Int): Array<CommunalWidgetContentModel?> {
+ return arrayOfNulls(size)
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/shared/model/GlanceableHubMultiUserHelper.kt b/packages/SystemUI/src/com/android/systemui/communal/shared/model/GlanceableHubMultiUserHelper.kt
new file mode 100644
index 000000000000..ef6a9ce6752b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/shared/model/GlanceableHubMultiUserHelper.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.communal.shared.model
+
+import android.os.UserHandle
+import android.os.UserManager
+import com.android.systemui.Flags.secondaryUserWidgetHost
+import com.android.systemui.dagger.SysUISingleton
+import javax.inject.Inject
+
+/** Helper for multi-user / HSUM related functionality for the Glanceable Hub. */
+interface GlanceableHubMultiUserHelper {
+ /** Whether the Glanceable Hub in HSUM flag is enabled. */
+ val glanceableHubHsumFlagEnabled: Boolean
+
+ /** Whether the device is in headless system user mode. */
+ fun isHeadlessSystemUserMode(): Boolean
+
+ /** Whether the current process is running in the headless system user. */
+ fun isInHeadlessSystemUser(): Boolean
+
+ /**
+ * Asserts that the current process is running in the headless system user.
+ *
+ * Only throws an exception if [glanceableHubHsumFlagEnabled] is true.
+ */
+ @Throws(IllegalStateException::class) fun assertInHeadlessSystemUser()
+
+ /**
+ * Asserts that the current process is NOT running in the headless system user.
+ *
+ * Only throws an exception if [glanceableHubHsumFlagEnabled] is true.
+ */
+ @Throws(IllegalStateException::class) fun assertNotInHeadlessSystemUser()
+}
+
+@SysUISingleton
+class GlanceableHubMultiUserHelperImpl @Inject constructor(private val userHandle: UserHandle) :
+ GlanceableHubMultiUserHelper {
+
+ override val glanceableHubHsumFlagEnabled: Boolean = secondaryUserWidgetHost()
+
+ override fun isHeadlessSystemUserMode(): Boolean = UserManager.isHeadlessSystemUserMode()
+
+ override fun isInHeadlessSystemUser(): Boolean {
+ return isHeadlessSystemUserMode() && userHandle.isSystem
+ }
+
+ override fun assertInHeadlessSystemUser() {
+ if (glanceableHubHsumFlagEnabled) {
+ check(isInHeadlessSystemUser())
+ }
+ }
+
+ override fun assertNotInHeadlessSystemUser() {
+ if (glanceableHubHsumFlagEnabled) {
+ check(!isInHeadlessSystemUser())
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/util/WidgetViewFactory.kt b/packages/SystemUI/src/com/android/systemui/communal/util/WidgetViewFactory.kt
index d5d3a927181b..cc6007b400f7 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/util/WidgetViewFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/util/WidgetViewFactory.kt
@@ -21,11 +21,14 @@ import android.os.Bundle
import android.util.SizeF
import com.android.app.tracing.coroutines.withContextTraced as withContext
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 javax.inject.Inject
import kotlin.coroutines.CoroutineContext
@@ -36,9 +39,11 @@ class WidgetViewFactory
constructor(
@UiBackground private val uiBgContext: CoroutineContext,
@UiBackground private val uiBgExecutor: Executor,
- private val appWidgetHost: CommunalAppWidgetHost,
+ 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,
@@ -51,9 +56,25 @@ constructor(
setExecutor(uiBgExecutor)
setAppWidget(model.appWidgetId, model.providerInfo)
}
- // 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.
- appWidgetHost.setListener(model.appWidgetId, listenerFactory.create(view))
+
+ 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(),
diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalAppWidgetHost.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalAppWidgetHost.kt
index 4f9ed2f7dbf8..70e7947e1b70 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalAppWidgetHost.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalAppWidgetHost.kt
@@ -18,6 +18,8 @@ package com.android.systemui.communal.widgets
import android.appwidget.AppWidgetHost
import android.content.Context
+import com.android.app.tracing.coroutines.launchTraced as launch
+import com.android.systemui.communal.shared.model.GlanceableHubMultiUserHelper
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.core.Logger
import javax.annotation.concurrent.GuardedBy
@@ -25,7 +27,6 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.asSharedFlow
-import com.android.app.tracing.coroutines.launchTraced as launch
/** Communal app widget host that creates a [CommunalAppWidgetHostView]. */
class CommunalAppWidgetHost(
@@ -33,7 +34,14 @@ class CommunalAppWidgetHost(
private val backgroundScope: CoroutineScope,
hostId: Int,
logBuffer: LogBuffer,
+ glanceableHubMultiUserHelper: GlanceableHubMultiUserHelper,
) : AppWidgetHost(context, hostId) {
+
+ init {
+ // The app widget host should never be accessed from a headless system user.
+ glanceableHubMultiUserHelper.assertNotInHeadlessSystemUser()
+ }
+
private val logger = Logger(logBuffer, TAG)
private val _appWidgetIdToRemove = MutableSharedFlow<Int>()
diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostStartable.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostStartable.kt
index 301da51c8082..dec7ba3a3eb1 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostStartable.kt
@@ -18,55 +18,118 @@ package com.android.systemui.communal.widgets
import com.android.systemui.CoreStartable
import com.android.systemui.communal.domain.interactor.CommunalInteractor
+import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor
import com.android.systemui.communal.shared.model.CommunalWidgetContentModel
+import com.android.systemui.communal.shared.model.GlanceableHubMultiUserHelper
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.settings.UserTracker
+import com.android.systemui.util.kotlin.BooleanFlowOperators.allOf
import com.android.systemui.util.kotlin.BooleanFlowOperators.anyOf
+import com.android.systemui.util.kotlin.BooleanFlowOperators.not
import com.android.systemui.util.kotlin.pairwise
import com.android.systemui.util.kotlin.sample
+import dagger.Lazy
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.dropWhile
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.withContext
+// Started per user, so make sure injections are lazy to avoid instantiating unnecessary
+// dependencies.
@SysUISingleton
class CommunalAppWidgetHostStartable
@Inject
constructor(
- private val appWidgetHost: CommunalAppWidgetHost,
- private val communalWidgetHost: CommunalWidgetHost,
- private val communalInteractor: CommunalInteractor,
- private val userTracker: UserTracker,
+ private val appWidgetHostLazy: Lazy<CommunalAppWidgetHost>,
+ private val communalWidgetHostLazy: Lazy<CommunalWidgetHost>,
+ private val communalInteractorLazy: Lazy<CommunalInteractor>,
+ private val communalSettingsInteractorLazy: Lazy<CommunalSettingsInteractor>,
+ private val keyguardInteractorLazy: Lazy<KeyguardInteractor>,
+ private val userTrackerLazy: Lazy<UserTracker>,
@Background private val bgScope: CoroutineScope,
- @Main private val uiDispatcher: CoroutineDispatcher
+ @Main private val uiDispatcher: CoroutineDispatcher,
+ private val glanceableHubWidgetManagerLazy: Lazy<GlanceableHubWidgetManager>,
+ private val glanceableHubMultiUserHelper: GlanceableHubMultiUserHelper,
) : CoreStartable {
+ private val appWidgetHost by lazy { appWidgetHostLazy.get() }
+ private val communalWidgetHost by lazy { communalWidgetHostLazy.get() }
+ private val communalInteractor by lazy { communalInteractorLazy.get() }
+ private val communalSettingsInteractor by lazy { communalSettingsInteractorLazy.get() }
+ private val keyguardInteractor by lazy { keyguardInteractorLazy.get() }
+ private val userTracker by lazy { userTrackerLazy.get() }
+ private val glanceableHubWidgetManager by lazy { glanceableHubWidgetManagerLazy.get() }
+
override fun start() {
- anyOf(communalInteractor.isCommunalAvailable, communalInteractor.editModeOpen)
- // Only trigger updates on state changes, ignoring the initial false value.
- .pairwise(false)
- .filter { (previous, new) -> previous != new }
- .onEach { (_, shouldListen) -> updateAppWidgetHostActive(shouldListen) }
- .sample(communalInteractor.communalWidgets, ::Pair)
- .onEach { (withPrev, widgets) ->
- val (_, isActive) = withPrev
- // The validation is performed once the hub becomes active.
- if (isActive) {
- validateWidgetsAndDeleteOrphaned(widgets)
+ if (
+ glanceableHubMultiUserHelper.glanceableHubHsumFlagEnabled &&
+ glanceableHubMultiUserHelper.isInHeadlessSystemUser()
+ ) {
+ onStartInHeadlessSystemUser()
+ } else {
+ onStartInForegroundUser()
+ }
+ }
+
+ private fun onStartInForegroundUser() {
+ // Make the host active when communal becomes available, and delete widgets whose user has
+ // been removed from the system.
+ // Skipped in HSUM, because lifecycle of the host is controlled by the
+ // [GlanceableHubWidgetManagerService], and widgets are stored per user so no more orphaned
+ // widgets.
+ if (
+ !glanceableHubMultiUserHelper.glanceableHubHsumFlagEnabled ||
+ !glanceableHubMultiUserHelper.isHeadlessSystemUserMode()
+ ) {
+ anyOf(communalInteractor.isCommunalAvailable, communalInteractor.editModeOpen)
+ // Only trigger updates on state changes, ignoring the initial false value.
+ .pairwise(false)
+ .filter { (previous, new) -> previous != new }
+ .onEach { (_, shouldListen) -> updateAppWidgetHostActive(shouldListen) }
+ .sample(communalInteractor.communalWidgets, ::Pair)
+ .onEach { (withPrev, widgets) ->
+ val (_, isActive) = withPrev
+ // The validation is performed once the hub becomes active.
+ if (isActive) {
+ validateWidgetsAndDeleteOrphaned(widgets)
+ }
}
- }
- .launchIn(bgScope)
+ .launchIn(bgScope)
+ }
appWidgetHost.appWidgetIdToRemove
.onEach { appWidgetId -> communalInteractor.deleteWidget(id = appWidgetId) }
.launchIn(bgScope)
}
+ private fun onStartInHeadlessSystemUser() {
+ // Connection to the widget manager service in the foreground user should stay open as long
+ // as the Glanceable Hub is available.
+ allOf(
+ communalSettingsInteractor.isCommunalEnabled,
+ not(keyguardInteractor.isEncryptedOrLockdown),
+ )
+ .distinctUntilChanged()
+ // Drop the initial false
+ .dropWhile { !it }
+ .onEach { shouldRegister ->
+ if (shouldRegister) {
+ glanceableHubWidgetManager.register()
+ } else {
+ glanceableHubWidgetManager.unregister()
+ }
+ }
+ .launchIn(bgScope)
+ }
+
private suspend fun updateAppWidgetHostActive(active: Boolean) =
// Always ensure this is called on the main/ui thread.
withContext(uiDispatcher) {
diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalWidgetHost.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalWidgetHost.kt
index 0b8d9775675f..8c745f59e918 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalWidgetHost.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalWidgetHost.kt
@@ -26,6 +26,8 @@ import android.os.Bundle
import android.os.UserHandle
import android.widget.RemoteViews
import androidx.annotation.WorkerThread
+import com.android.app.tracing.coroutines.launchTraced as launch
+import com.android.systemui.communal.shared.model.GlanceableHubMultiUserHelper
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.core.Logger
@@ -38,7 +40,6 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
-import com.android.app.tracing.coroutines.launchTraced as launch
/**
* Widget host that interacts with AppWidget service and host to bind and provide info for widgets
@@ -52,7 +53,14 @@ constructor(
private val appWidgetHost: CommunalAppWidgetHost,
private val selectedUserInteractor: SelectedUserInteractor,
@CommunalLog logBuffer: LogBuffer,
+ glanceableHubMultiUserHelper: GlanceableHubMultiUserHelper,
) : CommunalAppWidgetHost.Observer {
+
+ init {
+ // The communal widget host should never be accessed from a headless system user.
+ glanceableHubMultiUserHelper.assertNotInHeadlessSystemUser()
+ }
+
companion object {
private const val TAG = "CommunalWidgetHost"
@@ -96,7 +104,7 @@ constructor(
bindWidget(
widgetId = id,
user = user ?: UserHandle(selectedUserInteractor.getSelectedUserId()),
- provider = provider
+ provider = provider,
)
) {
logger.d("Successfully bound the widget $provider")
diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalWidgetModule.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalWidgetModule.kt
index f4962085000c..a235e7ae2b6a 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalWidgetModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalWidgetModule.kt
@@ -20,6 +20,7 @@ package com.android.systemui.communal.widgets
import android.appwidget.AppWidgetManager
import android.content.Context
import android.content.res.Resources
+import com.android.systemui.communal.shared.model.GlanceableHubMultiUserHelper
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
@@ -46,8 +47,15 @@ interface CommunalWidgetModule {
@Application context: Context,
@Background backgroundScope: CoroutineScope,
@CommunalLog logBuffer: LogBuffer,
+ glanceableHubMultiUserHelper: GlanceableHubMultiUserHelper,
): CommunalAppWidgetHost {
- return CommunalAppWidgetHost(context, backgroundScope, APP_WIDGET_HOST_ID, logBuffer)
+ return CommunalAppWidgetHost(
+ context,
+ backgroundScope,
+ APP_WIDGET_HOST_ID,
+ logBuffer,
+ glanceableHubMultiUserHelper,
+ )
}
@SysUISingleton
@@ -58,6 +66,7 @@ interface CommunalWidgetModule {
appWidgetHost: CommunalAppWidgetHost,
selectedUserInteractor: SelectedUserInteractor,
@CommunalLog logBuffer: LogBuffer,
+ glanceableHubMultiUserHelper: GlanceableHubMultiUserHelper,
): CommunalWidgetHost {
return CommunalWidgetHost(
applicationScope,
@@ -65,6 +74,7 @@ interface CommunalWidgetModule {
appWidgetHost,
selectedUserInteractor,
logBuffer,
+ glanceableHubMultiUserHelper,
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/GlanceableHubWidgetManager.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/GlanceableHubWidgetManager.kt
new file mode 100644
index 000000000000..202edf71fa11
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/GlanceableHubWidgetManager.kt
@@ -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 com.android.systemui.communal.widgets
+
+import android.appwidget.AppWidgetHost.AppWidgetHostListener
+import android.appwidget.AppWidgetProviderInfo
+import android.content.ComponentName
+import android.os.IBinder
+import android.os.UserHandle
+import android.widget.RemoteViews
+import com.android.server.servicewatcher.ServiceWatcher
+import com.android.server.servicewatcher.ServiceWatcher.ServiceListener
+import com.android.systemui.communal.shared.model.CommunalWidgetContentModel
+import com.android.systemui.communal.shared.model.GlanceableHubMultiUserHelper
+import com.android.systemui.communal.widgets.IGlanceableHubWidgetManagerService.IAppWidgetHostListener
+import com.android.systemui.communal.widgets.IGlanceableHubWidgetManagerService.IGlanceableHubWidgetsListener
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.core.Logger
+import com.android.systemui.log.dagger.CommunalLog
+import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
+import javax.inject.Inject
+import kotlinx.coroutines.channels.awaitClose
+
+/**
+ * Manages updates to Glanceable Hub widgets and requests to edit them from the headless system
+ * user.
+ *
+ * It communicates with the remote [GlanceableHubWidgetManagerService] which runs in the foreground
+ * user, and abstracts the IPC details from the rest of the system.
+ */
+@SysUISingleton
+class GlanceableHubWidgetManager
+@Inject
+constructor(
+ glanceableHubMultiUserHelper: GlanceableHubMultiUserHelper,
+ @CommunalLog logBuffer: LogBuffer,
+ serviceWatcherFactory: ServiceWatcherFactory<GlanceableHubWidgetManagerServiceInfo?>,
+) : ServiceListener<GlanceableHubWidgetManagerServiceInfo?> {
+
+ init {
+ // The manager should only be used in the headless system user.
+ glanceableHubMultiUserHelper.assertInHeadlessSystemUser()
+ }
+
+ private val logger = Logger(logBuffer, TAG)
+
+ private val serviceWatcher by lazy { serviceWatcherFactory.create(this) }
+
+ val widgets = conflatedCallbackFlow {
+ val callback =
+ object : IGlanceableHubWidgetsListener.Stub() {
+ override fun onWidgetsUpdated(widgets: List<CommunalWidgetContentModel>?) {
+ trySend(widgets ?: emptyList())
+ }
+ }
+ runOnService { service -> service.addWidgetsListener(callback) }
+ awaitClose { runOnService { service -> service.removeWidgetsListener(callback) } }
+ }
+
+ fun register() {
+ serviceWatcher.register()
+ }
+
+ fun unregister() {
+ serviceWatcher.unregister()
+ }
+
+ override fun onBind(binder: IBinder?, serviceInfo: GlanceableHubWidgetManagerServiceInfo?) {
+ logger.i("Service bound")
+ }
+
+ override fun onUnbind() {
+ logger.i("Service unbound")
+ }
+
+ /** Requests the foreground user to set a [AppWidgetHostListener] for the given app widget. */
+ fun setAppWidgetHostListener(appWidgetId: Int, listener: AppWidgetHostListener) =
+ runOnService { service ->
+ service.setAppWidgetHostListener(appWidgetId, createIAppWidgetHostListener(listener))
+ }
+
+ /** Requests the foreground user to add a widget. */
+ fun addWidget(
+ provider: ComponentName,
+ user: UserHandle,
+ rank: Int?,
+ configurator: WidgetConfigurator?,
+ ) = runOnService { service ->
+ // TODO(b/375036327): Add support for widget configuration
+ service.addWidget(provider, user, rank ?: -1)
+ }
+
+ /** Requests the foreground user to delete a widget. */
+ fun deleteWidget(appWidgetId: Int) = runOnService { service ->
+ service.deleteWidget(appWidgetId)
+ }
+
+ /** Requests the foreground user to update widget order. */
+ fun updateWidgetOrder(widgetIdToRankMap: Map<Int, Int>) = runOnService { service ->
+ service.updateWidgetOrder(
+ widgetIdToRankMap.keys.toIntArray(),
+ widgetIdToRankMap.values.toIntArray(),
+ )
+ }
+
+ /** Requests the foreground user to resize a widget. */
+ fun resizeWidget(appWidgetId: Int, spanY: Int, widgetIdToRankMap: Map<Int, Int>) =
+ runOnService { service ->
+ service.resizeWidget(
+ appWidgetId,
+ spanY,
+ widgetIdToRankMap.keys.toIntArray(),
+ widgetIdToRankMap.values.toIntArray(),
+ )
+ }
+
+ private fun runOnService(block: (IGlanceableHubWidgetManagerService) -> Unit) {
+ serviceWatcher.runOnBinder(
+ object : ServiceWatcher.BinderOperation {
+ override fun run(binder: IBinder?) {
+ block(IGlanceableHubWidgetManagerService.Stub.asInterface(binder))
+ }
+
+ override fun onError(t: Throwable?) {
+ // TODO(b/375236794): handle failure in case service is unbound
+ }
+ }
+ )
+ }
+
+ private fun createIAppWidgetHostListener(
+ listener: AppWidgetHostListener
+ ): IAppWidgetHostListener {
+ return object : IAppWidgetHostListener.Stub() {
+ override fun onUpdateProviderInfo(appWidget: AppWidgetProviderInfo?) {
+ listener.onUpdateProviderInfo(appWidget)
+ }
+
+ override fun updateAppWidget(views: RemoteViews?) {
+ listener.updateAppWidget(views)
+ }
+
+ override fun updateAppWidgetDeferred(packageName: String?, appWidgetId: Int) {
+ listener.updateAppWidgetDeferred(packageName, appWidgetId)
+ }
+
+ override fun onViewDataChanged(viewId: Int) {
+ listener.onViewDataChanged(viewId)
+ }
+ }
+ }
+
+ companion object {
+ private const val TAG = "GlanceableHubWidgetManager"
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/GlanceableHubWidgetManagerService.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/GlanceableHubWidgetManagerService.kt
new file mode 100644
index 000000000000..4d042fc277de
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/GlanceableHubWidgetManagerService.kt
@@ -0,0 +1,305 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.widgets
+
+import android.appwidget.AppWidgetHost.AppWidgetHostListener
+import android.appwidget.AppWidgetProviderInfo
+import android.content.ComponentName
+import android.content.Intent
+import android.os.IBinder
+import android.os.RemoteCallbackList
+import android.os.UserHandle
+import android.widget.RemoteViews
+import androidx.lifecycle.LifecycleService
+import androidx.lifecycle.lifecycleScope
+import com.android.systemui.communal.data.repository.CommunalWidgetRepository
+import com.android.systemui.communal.shared.model.GlanceableHubMultiUserHelper
+import com.android.systemui.communal.widgets.IGlanceableHubWidgetManagerService.IAppWidgetHostListener
+import com.android.systemui.communal.widgets.IGlanceableHubWidgetManagerService.IGlanceableHubWidgetsListener
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.core.Logger
+import com.android.systemui.log.dagger.CommunalLog
+import javax.inject.Inject
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.onEach
+
+/**
+ * Service for the [GlanceableHubWidgetManager], which runs in a foreground user in Headless System
+ * User Mode (HSUM), manages widgets as the user who owns them, and communicates back to the
+ * headless system user, where these widgets are rendered.
+ */
+class GlanceableHubWidgetManagerService
+@Inject
+constructor(
+ private val widgetRepository: CommunalWidgetRepository,
+ private val appWidgetHost: CommunalAppWidgetHost,
+ private val communalWidgetHost: CommunalWidgetHost,
+ glanceableHubMultiUserHelper: GlanceableHubMultiUserHelper,
+ @CommunalLog logBuffer: LogBuffer,
+) : LifecycleService() {
+
+ init {
+ // The service should only run in a foreground user.
+ glanceableHubMultiUserHelper.assertNotInHeadlessSystemUser()
+ }
+
+ private val logger = Logger(logBuffer, TAG)
+ private val widgetListenersRegistry = WidgetListenerRegistry()
+
+ override fun onCreate() {
+ super.onCreate()
+
+ logger.i("Service created")
+
+ communalWidgetHost.startObservingHost()
+ appWidgetHost.startListening()
+ }
+
+ override fun onDestroy() {
+ super.onDestroy()
+
+ logger.i("Service destroyed")
+
+ appWidgetHost.stopListening()
+ communalWidgetHost.stopObservingHost()
+
+ // Cancel all widget listener jobs and unregister listeners
+ widgetListenersRegistry.kill()
+ }
+
+ override fun onBind(intent: Intent): IBinder? {
+ super.onBind(intent)
+ return WidgetManagerServiceBinder().asBinder()
+ }
+
+ private fun addWidgetsListenerInternal(listener: IGlanceableHubWidgetsListener?) {
+ if (listener == null) {
+ throw IllegalStateException("Listener cannot be null")
+ }
+
+ if (!listener.asBinder().isBinderAlive) {
+ throw IllegalStateException("Listener binder is dead")
+ }
+
+ val job =
+ widgetRepository.communalWidgets
+ .onEach { widgets -> listener.onWidgetsUpdated(widgets) }
+ .launchIn(lifecycleScope)
+ widgetListenersRegistry.register(listener, job)
+ }
+
+ private fun removeWidgetsListenerInternal(listener: IGlanceableHubWidgetsListener?) {
+ if (listener == null) {
+ throw IllegalStateException("Listener cannot be null")
+ }
+
+ widgetListenersRegistry.unregister(listener)
+ }
+
+ private fun setAppWidgetHostListenerInternal(
+ appWidgetId: Int,
+ listener: IAppWidgetHostListener?,
+ ) {
+ if (listener == null) {
+ throw IllegalStateException("Listener cannot be null")
+ }
+
+ appWidgetHost.setListener(appWidgetId, createListener(listener))
+ }
+
+ private fun addWidgetInternal(provider: ComponentName?, user: UserHandle?, rank: Int) {
+ if (provider == null) {
+ throw IllegalStateException("Provider cannot be null")
+ }
+
+ if (user == null) {
+ throw IllegalStateException("User cannot be null")
+ }
+
+ // TODO(b/375036327): Add support for widget configuration
+ widgetRepository.addWidget(provider, user, rank, configurator = null)
+ }
+
+ private fun deleteWidgetInternal(appWidgetId: Int) {
+ widgetRepository.deleteWidget(appWidgetId)
+ }
+
+ private fun updateWidgetOrderInternal(appWidgetIds: IntArray?, ranks: IntArray?) {
+ if (appWidgetIds == null || ranks == null) {
+ throw IllegalStateException("appWidgetIds and ranks cannot be null")
+ }
+
+ if (appWidgetIds.size != ranks.size) {
+ throw IllegalStateException("appWidgetIds and ranks must be the same size")
+ }
+
+ widgetRepository.updateWidgetOrder(appWidgetIds.zip(ranks).toMap())
+ }
+
+ private fun resizeWidgetInternal(
+ appWidgetId: Int,
+ spanY: Int,
+ appWidgetIds: IntArray?,
+ ranks: IntArray?,
+ ) {
+ if (appWidgetIds == null || ranks == null) {
+ throw IllegalStateException("appWidgetIds and ranks cannot be null")
+ }
+
+ if (appWidgetIds.size != ranks.size) {
+ throw IllegalStateException("appWidgetIds and ranks must be the same size")
+ }
+
+ widgetRepository.resizeWidget(appWidgetId, spanY, appWidgetIds.zip(ranks).toMap())
+ }
+
+ private fun createListener(listener: IAppWidgetHostListener): AppWidgetHostListener {
+ return object : AppWidgetHostListener {
+ override fun onUpdateProviderInfo(appWidget: AppWidgetProviderInfo?) {
+ listener.onUpdateProviderInfo(appWidget)
+ }
+
+ override fun updateAppWidget(views: RemoteViews?) {
+ listener.updateAppWidget(views)
+ }
+
+ override fun updateAppWidgetDeferred(packageName: String?, appWidgetId: Int) {
+ listener.updateAppWidgetDeferred(packageName, appWidgetId)
+ }
+
+ override fun onViewDataChanged(viewId: Int) {
+ listener.onViewDataChanged(viewId)
+ }
+ }
+ }
+
+ private inner class WidgetManagerServiceBinder : IGlanceableHubWidgetManagerService.Stub() {
+ override fun addWidgetsListener(listener: IGlanceableHubWidgetsListener?) {
+ val iden = clearCallingIdentity()
+
+ try {
+ addWidgetsListenerInternal(listener)
+ } finally {
+ restoreCallingIdentity(iden)
+ }
+ }
+
+ override fun removeWidgetsListener(listener: IGlanceableHubWidgetsListener?) {
+ val iden = clearCallingIdentity()
+
+ try {
+ removeWidgetsListenerInternal(listener)
+ } finally {
+ restoreCallingIdentity(iden)
+ }
+ }
+
+ override fun setAppWidgetHostListener(appWidgetId: Int, listener: IAppWidgetHostListener?) {
+ val iden = clearCallingIdentity()
+
+ try {
+ setAppWidgetHostListenerInternal(appWidgetId, listener)
+ } finally {
+ restoreCallingIdentity(iden)
+ }
+ }
+
+ override fun addWidget(provider: ComponentName?, user: UserHandle?, rank: Int) {
+ val iden = clearCallingIdentity()
+
+ try {
+ addWidgetInternal(provider, user, rank)
+ } finally {
+ restoreCallingIdentity(iden)
+ }
+ }
+
+ override fun deleteWidget(appWidgetId: Int) {
+ val iden = clearCallingIdentity()
+
+ try {
+ deleteWidgetInternal(appWidgetId)
+ } finally {
+ restoreCallingIdentity(iden)
+ }
+ }
+
+ override fun updateWidgetOrder(appWidgetIds: IntArray?, ranks: IntArray?) {
+ val iden = clearCallingIdentity()
+
+ try {
+ updateWidgetOrderInternal(appWidgetIds, ranks)
+ } finally {
+ restoreCallingIdentity(iden)
+ }
+ }
+
+ override fun resizeWidget(
+ appWidgetId: Int,
+ spanY: Int,
+ appWidgetIds: IntArray?,
+ ranks: IntArray?,
+ ) {
+ val iden = clearCallingIdentity()
+
+ try {
+ resizeWidgetInternal(appWidgetId, spanY, appWidgetIds, ranks)
+ } finally {
+ restoreCallingIdentity(iden)
+ }
+ }
+ }
+
+ /**
+ * Registry of widget listener binders, which handles canceling the job associated with a
+ * listener when it is unregistered, or when the binder is dead.
+ */
+ private class WidgetListenerRegistry : RemoteCallbackList<IGlanceableHubWidgetsListener>() {
+ private val jobs = mutableMapOf<IGlanceableHubWidgetsListener, Job>()
+
+ fun register(listener: IGlanceableHubWidgetsListener, job: Job) {
+ if (register(listener)) {
+ synchronized(jobs) { jobs[listener] = job }
+ } else {
+ job.cancel()
+ }
+ }
+
+ override fun unregister(listener: IGlanceableHubWidgetsListener?): Boolean {
+ synchronized(jobs) { jobs.remove(listener)?.cancel() }
+ return super.unregister(listener)
+ }
+
+ override fun onCallbackDied(listener: IGlanceableHubWidgetsListener?) {
+ synchronized(jobs) { jobs.remove(listener)?.cancel() }
+ super.onCallbackDied(listener)
+ }
+
+ override fun kill() {
+ synchronized(jobs) {
+ jobs.values.forEach { it.cancel() }
+ jobs.clear()
+ }
+ super.kill()
+ }
+ }
+
+ companion object {
+ private const val TAG = "GlanceableHubWidgetManagerService"
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/GlanceableHubWidgetManagerServiceInfo.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/GlanceableHubWidgetManagerServiceInfo.kt
new file mode 100644
index 000000000000..d52761164a38
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/GlanceableHubWidgetManagerServiceInfo.kt
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.communal.widgets
+
+import android.content.ComponentName
+import android.content.Context
+import android.content.Context.BIND_AUTO_CREATE
+import android.os.UserHandle
+import com.android.server.servicewatcher.ServiceWatcher
+
+/**
+ * Information about the [GlanceableHubWidgetManagerServiceInfo] used for binding with the
+ * [ServiceWatcher].
+ */
+class GlanceableHubWidgetManagerServiceInfo(context: Context, userHandle: UserHandle) :
+ ServiceWatcher.BoundServiceInfo(
+ /* action= */ null,
+ UserHandle.getUid(userHandle.identifier, UserHandle.getCallingAppId()),
+ ComponentName(context.packageName, GlanceableHubWidgetManagerService::class.java.name),
+ BIND_AUTO_CREATE,
+ )
diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/GlanceableHubWidgetManagerServiceSupplier.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/GlanceableHubWidgetManagerServiceSupplier.kt
new file mode 100644
index 000000000000..ed77e6f5d7ea
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/GlanceableHubWidgetManagerServiceSupplier.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.communal.widgets
+
+import android.content.Context
+import com.android.server.servicewatcher.ServiceWatcher
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.settings.UserTracker
+import java.util.concurrent.Executor
+import javax.inject.Inject
+
+/**
+ * Supplies details about the [GlanceableHubWidgetManagerService] that the [ServiceWatcher] should
+ * bind to. Currently the service should only be bound if the current user is the main user.
+ */
+@SysUISingleton
+class GlanceableHubWidgetManagerServiceSupplier
+@Inject
+constructor(
+ @Application private val context: Context,
+ @Background private val bgExecutor: Executor,
+ private val userTracker: UserTracker,
+) : ServiceWatcher.ServiceSupplier<GlanceableHubWidgetManagerServiceInfo?>, UserTracker.Callback {
+
+ private var userAboutToSwitch = false
+ private var listener: ServiceWatcher.ServiceChangedListener? = null
+
+ override fun getServiceInfo(): GlanceableHubWidgetManagerServiceInfo? {
+ return GlanceableHubWidgetManagerServiceInfo(context, userTracker.userHandle)
+ }
+
+ override fun hasMatchingService(): Boolean {
+ // The service becomes unavailable immediately before a user switching is about to happen
+ // so that it is disconnected before the user process is terminated.
+ // It is also only available if the current user is the main user.
+ return !userAboutToSwitch && userTracker.userInfo.isMain
+ }
+
+ override fun register(listener: ServiceWatcher.ServiceChangedListener?) {
+ this.listener = listener
+ userTracker.addCallback(this, bgExecutor)
+ }
+
+ override fun unregister() {
+ listener = null
+ userTracker.removeCallback(this)
+ }
+
+ override fun onBeforeUserSwitching(newUser: Int) {
+ userAboutToSwitch = true
+ listener?.onServiceChanged()
+ }
+
+ override fun onUserChanged(newUser: Int, userContext: Context) {
+ userAboutToSwitch = false
+ listener?.onServiceChanged()
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/IGlanceableHubWidgetManagerService.aidl b/packages/SystemUI/src/com/android/systemui/communal/widgets/IGlanceableHubWidgetManagerService.aidl
new file mode 100644
index 000000000000..e556472c78a7
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/IGlanceableHubWidgetManagerService.aidl
@@ -0,0 +1,51 @@
+package com.android.systemui.communal.widgets;
+
+import android.appwidget.AppWidgetProviderInfo;
+import android.content.ComponentName;
+import android.os.UserHandle;
+import android.widget.RemoteViews;
+import com.android.systemui.communal.shared.model.CommunalWidgetContentModel;
+import java.util.List;
+
+// Interface for the [GlanceableHubWidgetManagerService], which runs in a foreground user in HSUM
+// and communicates with the headless system user.
+interface IGlanceableHubWidgetManagerService {
+
+ // Adds a listener for updates of Glanceable Hub widgets.
+ oneway void addWidgetsListener(in IGlanceableHubWidgetsListener listener);
+
+ // Removes a listener for updates of Glanceable Hub widgets.
+ oneway void removeWidgetsListener(in IGlanceableHubWidgetsListener listener);
+
+ // Sets a listener for updates on a specific widget.
+ oneway void setAppWidgetHostListener(int appWidgetId, in IAppWidgetHostListener listener);
+
+ // Requests to add a widget in the Glanceable Hub.
+ oneway void addWidget(in ComponentName provider, in UserHandle user, int rank);
+
+ // Requests to delete a widget from the Glanceable Hub.
+ oneway void deleteWidget(int appWidgetId);
+
+ // Requests to update the order of widgets in the Glanceable Hub.
+ oneway void updateWidgetOrder(in int[] appWidgetIds, in int[] ranks);
+
+ // Requests to resize a widget in the Glanceable Hub.
+ oneway void resizeWidget(int appWidgetId, int spanY, in int[] appWidgetIds, in int[] ranks);
+
+ // Listener for Glanceable Hub widget updates
+ oneway interface IGlanceableHubWidgetsListener {
+ // Called when widgets have updated.
+ void onWidgetsUpdated(in List<CommunalWidgetContentModel> widgets);
+ }
+
+ // Mirrors [AppWidgetHost#AppWidgetHostListener].
+ oneway interface IAppWidgetHostListener {
+ void onUpdateProviderInfo(in @nullable AppWidgetProviderInfo appWidget);
+
+ void updateAppWidget(in @nullable RemoteViews views);
+
+ void updateAppWidgetDeferred(in String packageName, int appWidgetId);
+
+ void onViewDataChanged(int viewId);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/ServiceWatcherFactory.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/ServiceWatcherFactory.kt
new file mode 100644
index 000000000000..f79cc87b19cb
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/ServiceWatcherFactory.kt
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.communal.widgets
+
+import android.content.Context
+import android.os.Handler
+import com.android.server.servicewatcher.ServiceWatcher
+import com.android.server.servicewatcher.ServiceWatcher.BoundServiceInfo
+import com.android.server.servicewatcher.ServiceWatcher.ServiceSupplier
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Background
+import javax.inject.Inject
+
+/** Factory for creating a [ServiceWatcher]. */
+interface ServiceWatcherFactory<TBoundService : BoundServiceInfo?> {
+ fun create(listener: ServiceWatcher.ServiceListener<TBoundService?>): ServiceWatcher
+}
+
+/** Implementation of the [ServiceWatcherFactory] for the [GlanceableHubWidgetManagerService]. */
+@SysUISingleton
+class GlanceableHubWidgetManagerServiceWatcherFactoryImpl
+@Inject
+constructor(
+ @Application private val context: Context,
+ @Background private val handler: Handler,
+ private val supplier: ServiceSupplier<GlanceableHubWidgetManagerServiceInfo?>,
+) : ServiceWatcherFactory<GlanceableHubWidgetManagerServiceInfo?> {
+
+ override fun create(
+ listener: ServiceWatcher.ServiceListener<GlanceableHubWidgetManagerServiceInfo?>
+ ): ServiceWatcher {
+ return ServiceWatcher.create(
+ context,
+ handler,
+ GlanceableHubWidgetManagerService::class.java.simpleName,
+ supplier,
+ listener,
+ )
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/WidgetConfigurationController.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/WidgetConfigurationController.kt
index 3e68479e717a..d157cd7acb76 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/widgets/WidgetConfigurationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/WidgetConfigurationController.kt
@@ -21,8 +21,10 @@ import android.app.ActivityOptions
import android.content.ActivityNotFoundException
import android.window.SplashScreen
import androidx.activity.ComponentActivity
+import com.android.systemui.communal.shared.model.GlanceableHubMultiUserHelper
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.util.nullableAtomicReference
+import dagger.Lazy
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
@@ -38,8 +40,9 @@ class WidgetConfigurationController
@AssistedInject
constructor(
@Assisted private val activity: ComponentActivity,
- private val appWidgetHost: CommunalAppWidgetHost,
- @Background private val bgDispatcher: CoroutineDispatcher
+ private val appWidgetHostLazy: Lazy<CommunalAppWidgetHost>,
+ @Background private val bgDispatcher: CoroutineDispatcher,
+ private val glanceableHubMultiUserHelper: GlanceableHubMultiUserHelper,
) : WidgetConfigurator {
@AssistedFactory
fun interface Factory {
@@ -62,13 +65,21 @@ constructor(
}
try {
- appWidgetHost.startAppWidgetConfigureActivityForResult(
- activity,
- appWidgetId,
- 0,
- REQUEST_CODE,
- options.toBundle()
- )
+ // TODO(b/375036327): Add support for widget configuration
+ if (
+ !glanceableHubMultiUserHelper.glanceableHubHsumFlagEnabled ||
+ !glanceableHubMultiUserHelper.isInHeadlessSystemUser()
+ ) {
+ with(appWidgetHostLazy.get()) {
+ startAppWidgetConfigureActivityForResult(
+ activity,
+ appWidgetId,
+ 0,
+ REQUEST_CODE,
+ options.toBundle(),
+ )
+ }
+ }
} catch (e: ActivityNotFoundException) {
setConfigurationResult(Activity.RESULT_CANCELED)
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DefaultServiceBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/DefaultServiceBinder.java
index 904d5898fcf8..8f01775bc969 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/DefaultServiceBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/DefaultServiceBinder.java
@@ -19,6 +19,7 @@ package com.android.systemui.dagger;
import android.app.Service;
import com.android.systemui.SystemUIService;
+import com.android.systemui.communal.widgets.GlanceableHubWidgetManagerService;
import com.android.systemui.doze.DozeService;
import com.android.systemui.dreams.DreamOverlayService;
import com.android.systemui.dump.SystemUIAuxiliaryDumpService;
@@ -93,4 +94,10 @@ public abstract class DefaultServiceBinder {
@ClassKey(IssueRecordingService.class)
public abstract Service bindIssueRecordingService(IssueRecordingService service);
+ /** Inject into GlanceableHubWidgetManagerService */
+ @Binds
+ @IntoMap
+ @ClassKey(GlanceableHubWidgetManagerService.class)
+ public abstract Service bindGlanceableHubWidgetManagerService(
+ GlanceableHubWidgetManagerService service);
}
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 eddac4d4ad73..05ff0cc30a44 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
@@ -70,7 +70,7 @@ class MultitaskingShortcutsSource @Inject constructor(@Main private val resource
},
// Change split screen focus to LHS:
// - Meta + Alt + Left arrow
- shortcutInfo(resources.getString(R.string.system_multitasking_splitscreen_focus_rhs)) {
+ shortcutInfo(resources.getString(R.string.system_multitasking_splitscreen_focus_lhs)) {
command(META_META_ON or META_ALT_ON, KEYCODE_DPAD_LEFT)
},
)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
index f3eeed2682a0..8c6037107c5a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
@@ -19,7 +19,6 @@ package com.android.systemui.keyguard.domain.interactor
import android.animation.ValueAnimator
import android.util.MathUtils
import com.android.app.animation.Interpolators
-import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.systemui.Flags.communalSceneKtfRefactor
import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor
import com.android.systemui.dagger.SysUISingleton
@@ -40,17 +39,19 @@ import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.shade.data.repository.ShadeRepository
import com.android.systemui.util.kotlin.sample
-import java.util.UUID
-import javax.inject.Inject
-import kotlin.time.Duration.Companion.milliseconds
-import kotlin.time.Duration.Companion.seconds
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onStart
+import kotlin.time.Duration.Companion.milliseconds
+import kotlin.time.Duration.Companion.seconds
+import java.util.UUID
+import javax.inject.Inject
+import com.android.app.tracing.coroutines.launchTraced as launch
@SysUISingleton
class FromLockscreenTransitionInteractor
@@ -258,6 +259,19 @@ constructor(
}
}
}
+
+ // Ensure that transitionId is nulled out if external signals cause a PRIMARY_BOUNCER
+ // transition to be canceled.
+ scope.launch {
+ transitionInteractor.transitions
+ .filter {
+ it.transitionState == TransitionState.CANCELED &&
+ it.to == KeyguardState.PRIMARY_BOUNCER
+ }
+ .collect {
+ transitionId = null
+ }
+ }
}
fun dismissKeyguard() {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSImpl.java
index 0b37b5b7be3d..1ca3927ace33 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSImpl.java
@@ -1146,4 +1146,4 @@ public class QSImpl implements QS, CommandQueue.Callbacks, StatusBarStateControl
updateState();
}
}
-}
+} \ No newline at end of file
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 9c8e84f280a0..bacff99fe048 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt
@@ -38,12 +38,14 @@ import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.togetherWith
import androidx.compose.foundation.ScrollState
+import androidx.compose.foundation.layout.Arrangement.spacedBy
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.navigationBars
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.windowInsetsPadding
@@ -73,13 +75,14 @@ import androidx.compose.ui.semantics.CustomAccessibilityAction
import androidx.compose.ui.semantics.customActions
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.unit.IntOffset
+import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.round
import androidx.compose.ui.util.fastRoundToInt
+import androidx.compose.ui.viewinterop.AndroidView
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
-import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.compose.animation.scene.ContentKey
import com.android.compose.animation.scene.ElementKey
import com.android.compose.animation.scene.ElementMatcher
@@ -98,10 +101,7 @@ import com.android.systemui.compose.modifiers.sysuiResTag
import com.android.systemui.dump.DumpManager
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.lifecycle.setSnapshotBinding
-import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager
import com.android.systemui.media.controls.ui.view.MediaHost
-import com.android.systemui.media.dagger.MediaModule.QS_PANEL
-import com.android.systemui.media.dagger.MediaModule.QUICK_QS_PANEL
import com.android.systemui.plugins.qs.QS
import com.android.systemui.plugins.qs.QSContainerController
import com.android.systemui.qs.composefragment.SceneKeys.QuickQuickSettings
@@ -127,7 +127,6 @@ import com.android.systemui.util.println
import java.io.PrintWriter
import java.util.function.Consumer
import javax.inject.Inject
-import javax.inject.Named
import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.awaitCancellation
import kotlinx.coroutines.coroutineScope
@@ -135,6 +134,7 @@ import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.launch
@SuppressLint("ValidFragment")
class QSFragmentCompose
@@ -142,11 +142,11 @@ class QSFragmentCompose
constructor(
private val qsFragmentComposeViewModelFactory: QSFragmentComposeViewModel.Factory,
private val dumpManager: DumpManager,
- @Named(QUICK_QS_PANEL) private val qqsMediaHost: MediaHost,
- @Named(QS_PANEL) private val qsMediaHost: MediaHost,
) : LifecycleFragment(), QS, Dumpable {
private val scrollListener = MutableStateFlow<QS.ScrollListener?>(null)
+ private val collapsedMediaVisibilityChangedListener =
+ MutableStateFlow<(Consumer<Boolean>)?>(null)
private val heightListener = MutableStateFlow<QS.HeightListener?>(null)
private val qsContainerController = MutableStateFlow<QSContainerController?>(null)
@@ -183,8 +183,6 @@ constructor(
QSComposeFragment.isUnexpectedlyInLegacyMode()
viewModel = qsFragmentComposeViewModelFactory.create(lifecycleScope)
- qqsMediaHost.init(MediaHierarchyManager.LOCATION_QQS)
- qsMediaHost.init(MediaHierarchyManager.LOCATION_QS)
setListenerCollections()
lifecycleScope.launch { viewModel.activate() }
}
@@ -491,7 +489,7 @@ constructor(
}
override fun setCollapsedMediaVisibilityChangedListener(listener: Consumer<Boolean>?) {
- // TODO (b/353253280)
+ collapsedMediaVisibilityChangedListener.value = listener
}
override fun setScrollListener(scrollListener: QS.ScrollListener?) {
@@ -534,6 +532,7 @@ constructor(
lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
this@QSFragmentCompose.view?.setSnapshotBinding {
scrollListener.value?.onQsPanelScrollChanged(scrollState.value)
+ collapsedMediaVisibilityChangedListener.value?.accept(viewModel.qqsMediaVisible)
}
launch {
setListenerJob(
@@ -569,7 +568,7 @@ constructor(
.squishiness
.collectAsStateWithLifecycle()
- Column(modifier = Modifier.sysuiResTag("quick_qs_panel")) {
+ Column(modifier = Modifier.sysuiResTag(ResIdTags.quickQsPanel)) {
Box(
modifier =
Modifier.fillMaxWidth()
@@ -581,6 +580,9 @@ constructor(
leftFromRoot + coordinates.size.width,
topFromRoot + coordinates.size.height,
)
+ if (squishiness == 1f) {
+ viewModel.qqsHeight = coordinates.size.height
+ }
}
// Use an approach layout to determien the height without squishiness, as
// that's the value that NPVC and QuickSettingsController care about
@@ -595,8 +597,7 @@ constructor(
.padding(top = { qqsPadding }, bottom = { bottomPadding })
) {
if (viewModel.isQsEnabled) {
- QuickQuickSettings(
- viewModel = viewModel.containerViewModel.quickQuickSettingsViewModel,
+ Column(
modifier =
Modifier.collapseExpandSemanticAction(
stringResource(
@@ -608,7 +609,16 @@ constructor(
QuickSettingsShade.Dimensions.Padding.roundToPx()
}
),
- )
+ verticalArrangement =
+ spacedBy(dimensionResource(R.dimen.qs_tile_margin_vertical)),
+ ) {
+ QuickQuickSettings(
+ viewModel = viewModel.containerViewModel.quickQuickSettingsViewModel
+ )
+ if (viewModel.qqsMediaVisible) {
+ MediaObject(mediaHost = viewModel.qqsMediaHost)
+ }
+ }
}
}
Spacer(modifier = Modifier.weight(1f))
@@ -645,14 +655,27 @@ constructor(
}
.onSizeChanged { viewModel.qsScrollHeight = it.height }
.verticalScroll(scrollState)
+ .sysuiResTag(ResIdTags.qsScroll)
) {
Spacer(
modifier = Modifier.height { qqsPadding + qsExtraPadding.roundToPx() }
)
QuickSettingsLayout(
viewModel = viewModel.containerViewModel,
- modifier = Modifier.sysuiResTag("quick_settings_panel"),
+ modifier = Modifier.sysuiResTag(ResIdTags.quickSettingsPanel),
)
+ Spacer(modifier = Modifier.height(8.dp))
+ if (viewModel.qsMediaVisible) {
+ MediaObject(
+ mediaHost = viewModel.qsMediaHost,
+ modifier =
+ Modifier.padding(
+ horizontal = {
+ QuickSettingsShade.Dimensions.Padding.roundToPx()
+ }
+ ),
+ )
+ }
}
}
QuickSettingsTheme {
@@ -660,7 +683,7 @@ constructor(
viewModel = viewModel.footerActionsViewModel,
qsVisibilityLifecycleOwner = this@QSFragmentCompose,
modifier =
- Modifier.sysuiResTag("qs_footer_actions")
+ Modifier.sysuiResTag(ResIdTags.qsFooterActions)
.element(ElementKeys.FooterActions),
)
}
@@ -914,3 +937,29 @@ private fun Modifier.gesturesDisabled(disabled: Boolean) =
} else {
this
}
+
+@Composable
+private fun MediaObject(mediaHost: MediaHost, modifier: Modifier = Modifier) {
+ Box {
+ AndroidView(
+ modifier = modifier,
+ factory = {
+ mediaHost.hostView.apply {
+ layoutParams =
+ FrameLayout.LayoutParams(
+ FrameLayout.LayoutParams.MATCH_PARENT,
+ FrameLayout.LayoutParams.WRAP_CONTENT,
+ )
+ }
+ },
+ onReset = {},
+ )
+ }
+}
+
+private object ResIdTags {
+ const val quickSettingsPanel = "quick_settings_panel"
+ const val quickQsPanel = "quick_qs_panel"
+ const val qsScroll = "expanded_qs_scroll_view"
+ const val qsFooterActions = "qs_footer_actions"
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/composefragment/dagger/QSFragmentComposeModule.kt b/packages/SystemUI/src/com/android/systemui/qs/composefragment/dagger/QSFragmentComposeModule.kt
new file mode 100644
index 000000000000..512732090036
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/composefragment/dagger/QSFragmentComposeModule.kt
@@ -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.systemui.qs.composefragment.dagger
+
+import android.content.Context
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.util.Utils
+import dagger.Module
+import dagger.Provides
+import javax.inject.Named
+
+@Module
+interface QSFragmentComposeModule {
+
+ companion object {
+ const val QS_USING_MEDIA_PLAYER = "compose_fragment_using_media_player"
+
+ @Provides
+ @SysUISingleton
+ @Named(QS_USING_MEDIA_PLAYER)
+ fun providesUsingMedia(@Application context: Context): Boolean {
+ return Utils.useQsMediaPlayer(context)
+ }
+ }
+}
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 d30c6bec87dd..0ca621d7d2e2 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
@@ -38,11 +38,17 @@ import com.android.systemui.keyguard.shared.model.Edge
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.lifecycle.ExclusiveActivatable
import com.android.systemui.lifecycle.Hydrator
+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.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.composefragment.dagger.QSFragmentComposeModule
import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsViewModel
import com.android.systemui.qs.panels.domain.interactor.TileSquishinessInteractor
-import com.android.systemui.qs.panels.ui.viewmodel.PaginatedGridViewModel
+import com.android.systemui.qs.panels.ui.viewmodel.InFirstPageViewModel
import com.android.systemui.qs.ui.viewmodel.QuickSettingsContainerViewModel
import com.android.systemui.res.R
import com.android.systemui.scene.shared.model.Scenes
@@ -60,10 +66,14 @@ import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import java.io.PrintWriter
+import javax.inject.Named
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.awaitCancellation
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.coroutineScope
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.callbackFlow
+import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onStart
@@ -83,7 +93,10 @@ constructor(
configurationInteractor: ConfigurationInteractor,
private val largeScreenHeaderHelper: LargeScreenHeaderHelper,
private val squishinessInteractor: TileSquishinessInteractor,
- private val paginatedGridViewModel: PaginatedGridViewModel,
+ private val inFirstPageViewModel: InFirstPageViewModel,
+ @Named(QUICK_QS_PANEL) val qqsMediaHost: MediaHost,
+ @Named(QS_PANEL) val qsMediaHost: MediaHost,
+ @Named(QSFragmentComposeModule.QS_USING_MEDIA_PLAYER) private val usingMedia: Boolean,
@Assisted private val lifecycleScope: LifecycleCoroutineScope,
) : Dumpable, ExclusiveActivatable() {
@@ -191,7 +204,7 @@ constructor(
var collapseExpandAccessibilityAction: Runnable? = null
val inFirstPage: Boolean
- get() = paginatedGridViewModel.inFirstPage
+ get() = inFirstPageViewModel.inFirstPage
var overScrollAmount by mutableStateOf(0)
@@ -222,6 +235,30 @@ constructor(
}
}
+ val showingMirror: Boolean
+ get() = containerViewModel.brightnessSliderViewModel.showMirror
+
+ // The initial values in these two are not meaningful. The flow will emit on start the correct
+ // values. This is because we need to lazily fetch them after initMediaHosts.
+ val qqsMediaVisible by
+ hydrator.hydratedStateOf(
+ traceName = "qqsMediaVisible",
+ initialValue = usingMedia,
+ source =
+ if (usingMedia) {
+ mediaHostVisible(qqsMediaHost)
+ } else {
+ flowOf(false)
+ },
+ )
+
+ val qsMediaVisible by
+ hydrator.hydratedStateOf(
+ traceName = "qsMediaVisible",
+ initialValue = usingMedia,
+ source = if (usingMedia) mediaHostVisible(qsMediaHost) else flowOf(false),
+ )
+
private var qsBounds by mutableStateOf(Rect())
private val constrainedSquishinessFraction: Float
@@ -259,9 +296,6 @@ constructor(
.onStart { emit(sysuiStatusBarStateController.state) },
)
- val showingMirror: Boolean
- get() = containerViewModel.brightnessSliderViewModel.showMirror
-
private val isKeyguardState: Boolean
get() = statusBarState == StatusBarState.KEYGUARD
@@ -323,6 +357,7 @@ constructor(
)
override suspend fun onActivated(): Nothing {
+ initMediaHosts() // init regardless of using media (same as current QS).
coroutineScope {
launch { hydrateSquishinessInteractor() }
launch { hydrator.activate() }
@@ -331,6 +366,19 @@ constructor(
}
}
+ private fun initMediaHosts() {
+ qqsMediaHost.apply {
+ expansion = MediaHostState.EXPANDED
+ showsOnlyActiveMedia = true
+ init(MediaHierarchyManager.LOCATION_QQS)
+ }
+ qsMediaHost.apply {
+ expansion = MediaHostState.EXPANDED
+ showsOnlyActiveMedia = false
+ init(MediaHierarchyManager.LOCATION_QS)
+ }
+ }
+
private suspend fun hydrateSquishinessInteractor() {
snapshotFlow { constrainedSquishinessFraction }
.collect { squishinessInteractor.setSquishinessValue(it) }
@@ -373,6 +421,10 @@ constructor(
println("qqsHeight", "${qqsHeight}px")
println("qsScrollHeight", "${qsScrollHeight}px")
}
+ printSection("Media") {
+ println("qqsMediaVisible", qqsMediaVisible)
+ println("qsMediaVisible", qsMediaVisible)
+ }
}
}
@@ -390,3 +442,21 @@ private fun Float.constrainSquishiness(): Float {
}
private val SHORT_PARALLAX_AMOUNT = 0.1f
+
+/**
+ * Returns a flow to track the visibility of a [MediaHost]. The flow will emit on start the visible
+ * state of the view.
+ */
+private fun mediaHostVisible(mediaHost: MediaHost): Flow<Boolean> {
+ return callbackFlow {
+ val listener: (Boolean) -> Unit = { visible: Boolean -> trySend(visible) }
+ mediaHost.addVisibilityChangeListener(listener)
+
+ awaitClose { mediaHost.removeVisibilityChangeListener(listener) }
+ }
+ // Need to use this to set initial state because on creation of the media host, the
+ // view visibility is not in sync with [MediaHost.visible], which is what we track with
+ // the listener. The correct state is set as part of init, so we need to get the state
+ // lazily.
+ .onStart { emit(mediaHost.visible) }
+}
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 29bcad4e0e0c..94b8a3ac5a3c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java
@@ -19,6 +19,7 @@ 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;
@@ -40,6 +41,7 @@ import dagger.multibindings.Multibinds;
includes = {
MediaModule.class,
PanelsModule.class,
+ QSFragmentComposeModule.class,
QSExternalModule.class,
QSFlagsModule.class,
QSHostModule.class,
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 d55763aaeddb..6cc2cbc63d09 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
@@ -40,9 +40,9 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
-import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.compose.animation.scene.SceneScope
import com.android.systemui.compose.modifiers.sysuiResTag
+import com.android.systemui.lifecycle.rememberViewModel
import com.android.systemui.qs.panels.dagger.PaginatedBaseLayoutType
import com.android.systemui.qs.panels.ui.compose.PaginatedGridLayout.Dimensions.FooterHeight
import com.android.systemui.qs.panels.ui.compose.PaginatedGridLayout.Dimensions.InterPageSpacing
@@ -54,7 +54,7 @@ import javax.inject.Inject
class PaginatedGridLayout
@Inject
constructor(
- private val viewModel: PaginatedGridViewModel,
+ private val viewModelFactory: PaginatedGridViewModel.Factory,
@PaginatedBaseLayoutType private val delegateGridLayout: PaginatableGridLayout,
) : GridLayout by delegateGridLayout {
@Composable
@@ -63,13 +63,18 @@ constructor(
modifier: Modifier,
editModeStart: () -> Unit,
) {
+ val viewModel =
+ rememberViewModel(traceName = "PaginatedGridLayout-TileGrid") {
+ viewModelFactory.create()
+ }
+
DisposableEffect(tiles) {
val token = Any()
tiles.forEach { it.startListening(token) }
onDispose { tiles.forEach { it.stopListening(token) } }
}
- val columns by viewModel.columns.collectAsStateWithLifecycle()
- val rows by viewModel.rows.collectAsStateWithLifecycle()
+ val columns by viewModel.columns
+ val rows = viewModel.rows
val pages =
remember(tiles, columns, rows) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/QuickQuickSettings.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/QuickQuickSettings.kt
index 99a6cda8cbf5..ca28ab3e6ce3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/QuickQuickSettings.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/QuickQuickSettings.kt
@@ -41,7 +41,8 @@ fun SceneScope.QuickQuickSettings(
viewModel: QuickQuickSettingsViewModel,
modifier: Modifier = Modifier,
) {
- val sizedTiles by viewModel.tileViewModels.collectAsStateWithLifecycle()
+
+ val sizedTiles = viewModel.tileViewModels
val tiles = sizedTiles.fastMap { it.tile }
val bounceables = remember(sizedTiles) { List(sizedTiles.size) { BounceableTileViewModel() } }
val squishiness by viewModel.squishinessViewModel.squishiness.collectAsStateWithLifecycle()
@@ -52,7 +53,7 @@ fun SceneScope.QuickQuickSettings(
tiles.forEach { it.startListening(token) }
onDispose { tiles.forEach { it.stopListening(token) } }
}
- val columns by viewModel.columns.collectAsStateWithLifecycle()
+ val columns = viewModel.columns
var cellIndex = 0
Box(modifier = modifier) {
GridAnchor()
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/InfiniteGridLayout.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/InfiniteGridLayout.kt
index 91f2da2b90f7..19ab29e6c796 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/InfiniteGridLayout.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/InfiniteGridLayout.kt
@@ -72,7 +72,7 @@ constructor(
rememberViewModel(traceName = "InfiniteGridLayout.TileGrid") {
viewModel.dynamicIconTilesViewModelFactory.create()
}
- val columns by viewModel.gridSizeViewModel.columns.collectAsStateWithLifecycle()
+ val columns by viewModel.gridSizeViewModel.columns
val sizedTiles = tiles.map { SizedTileImpl(it, it.spec.width()) }
val bounceables =
remember(sizedTiles) { List(sizedTiles.size) { BounceableTileViewModel() } }
@@ -118,7 +118,7 @@ constructor(
rememberViewModel(traceName = "InfiniteGridLayout.EditTileGrid") {
viewModel.dynamicIconTilesViewModelFactory.create()
}
- val columns by viewModel.gridSizeViewModel.columns.collectAsStateWithLifecycle()
+ val columns by viewModel.gridSizeViewModel.columns
val largeTiles by iconTilesViewModel.largeTiles.collectAsStateWithLifecycle()
// Non-current tiles should always be displayed as icon tiles.
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/InFirstPageViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/InFirstPageViewModel.kt
new file mode 100644
index 000000000000..225f54235d47
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/InFirstPageViewModel.kt
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.panels.ui.viewmodel
+
+import com.android.systemui.dagger.SysUISingleton
+import javax.inject.Inject
+
+/*
+ * Tracks whether the current HorizontalPager (using this viewmodel) is in the first page.
+ * This requires it to be a `@SysUISingleton` to be shared between viewmodels.
+ */
+@SysUISingleton
+class InFirstPageViewModel @Inject constructor() {
+ var inFirstPage = true
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/InfiniteGridViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/InfiniteGridViewModel.kt
index 0d1206768db1..d68710048e13 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/InfiniteGridViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/InfiniteGridViewModel.kt
@@ -16,6 +16,7 @@
package com.android.systemui.qs.panels.ui.viewmodel
+import com.android.systemui.lifecycle.ExclusiveActivatable
import com.android.systemui.qs.panels.ui.dialog.QSResetDialogDelegate
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
@@ -27,12 +28,16 @@ constructor(
val gridSizeViewModel: QSColumnsViewModel,
val squishinessViewModel: TileSquishinessViewModel,
private val resetDialogDelegate: QSResetDialogDelegate,
-) {
+) : ExclusiveActivatable() {
fun showResetDialog() {
resetDialogDelegate.showDialog()
}
+ override suspend fun onActivated(): Nothing {
+ gridSizeViewModel.activate()
+ }
+
@AssistedFactory
interface Factory {
fun create(): InfiniteGridViewModel
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 0f7dafcd941c..8bd9ed05c12c 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
@@ -16,33 +16,50 @@
package com.android.systemui.qs.panels.ui.viewmodel
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Application
+import androidx.compose.runtime.State
+import androidx.compose.runtime.getValue
+import com.android.systemui.lifecycle.ExclusiveActivatable
+import com.android.systemui.lifecycle.Hydrator
import com.android.systemui.qs.panels.domain.interactor.PaginatedGridInteractor
-import javax.inject.Inject
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.flow.SharingStarted
-import kotlinx.coroutines.flow.stateIn
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+import kotlinx.coroutines.awaitCancellation
+import kotlinx.coroutines.coroutineScope
+import kotlinx.coroutines.launch
-@SysUISingleton
class PaginatedGridViewModel
-@Inject
+@AssistedInject
constructor(
iconTilesViewModel: IconTilesViewModel,
- gridSizeViewModel: QSColumnsViewModel,
+ private val gridSizeViewModel: QSColumnsViewModel,
paginatedGridInteractor: PaginatedGridInteractor,
- @Application applicationScope: CoroutineScope,
-) : IconTilesViewModel by iconTilesViewModel, QSColumnsViewModel by gridSizeViewModel {
- val rows =
- paginatedGridInteractor.rows.stateIn(
- applicationScope,
- SharingStarted.WhileSubscribed(),
- paginatedGridInteractor.defaultRows,
+ inFirstPageViewModel: InFirstPageViewModel,
+) : IconTilesViewModel by iconTilesViewModel, ExclusiveActivatable() {
+
+ private val hydrator = Hydrator("PaginatedGridViewModel")
+
+ val rows by
+ hydrator.hydratedStateOf(
+ traceName = "rows",
+ initialValue = paginatedGridInteractor.defaultRows,
+ source = paginatedGridInteractor.rows,
)
- /*
- * Tracks whether the current HorizontalPager (using this viewmodel) is in the first page.
- * This requires it to be a `@SysUISingleton` to be shared between viewmodels.
- */
- var inFirstPage = true
+ var inFirstPage by inFirstPageViewModel::inFirstPage
+
+ val columns: State<Int>
+ get() = gridSizeViewModel.columns
+
+ override suspend fun onActivated(): Nothing {
+ coroutineScope {
+ launch { hydrator.activate() }
+ launch { gridSizeViewModel.activate() }
+ awaitCancellation()
+ }
+ }
+
+ @AssistedFactory
+ interface Factory {
+ fun create(): PaginatedGridViewModel
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/QSColumnsViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/QSColumnsViewModel.kt
index 0f1c77eb4711..8926d2ff107e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/QSColumnsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/QSColumnsViewModel.kt
@@ -16,17 +16,25 @@
package com.android.systemui.qs.panels.ui.viewmodel
-import com.android.systemui.dagger.SysUISingleton
+import androidx.compose.runtime.State
+import com.android.systemui.lifecycle.Activatable
+import com.android.systemui.lifecycle.ExclusiveActivatable
+import com.android.systemui.lifecycle.Hydrator
import com.android.systemui.qs.panels.domain.interactor.QSColumnsInteractor
import javax.inject.Inject
-import kotlinx.coroutines.flow.StateFlow
-interface QSColumnsViewModel {
- val columns: StateFlow<Int>
+interface QSColumnsViewModel : Activatable {
+ val columns: State<Int>
}
-@SysUISingleton
class QSColumnsSizeViewModelImpl @Inject constructor(interactor: QSColumnsInteractor) :
- QSColumnsViewModel {
- override val columns: StateFlow<Int> = interactor.columns
+ QSColumnsViewModel, ExclusiveActivatable() {
+ private val hydrator = Hydrator("QSColumnsSizeViewModelImpl")
+
+ override val columns =
+ hydrator.hydratedStateOf(traceName = "columns", source = interactor.columns)
+
+ override suspend fun onActivated(): Nothing {
+ hydrator.activate()
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/QuickQuickSettingsViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/QuickQuickSettingsViewModel.kt
index 887a70f39f6a..0859c86d74e1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/QuickQuickSettingsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/QuickQuickSettingsViewModel.kt
@@ -16,67 +16,69 @@
package com.android.systemui.qs.panels.ui.viewmodel
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Application
+import androidx.compose.runtime.derivedStateOf
+import androidx.compose.runtime.getValue
import com.android.systemui.haptics.msdl.qs.TileHapticsViewModelFactoryProvider
+import com.android.systemui.lifecycle.ExclusiveActivatable
+import com.android.systemui.lifecycle.Hydrator
import com.android.systemui.qs.panels.domain.interactor.QuickQuickSettingsRowInteractor
-import com.android.systemui.qs.panels.shared.model.SizedTile
import com.android.systemui.qs.panels.shared.model.SizedTileImpl
import com.android.systemui.qs.panels.shared.model.splitInRowsSequence
import com.android.systemui.qs.pipeline.domain.interactor.CurrentTilesInteractor
import com.android.systemui.qs.pipeline.shared.TileSpec
-import javax.inject.Inject
-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.flatMapLatest
-import kotlinx.coroutines.flow.mapLatest
-import kotlinx.coroutines.flow.stateIn
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+import kotlinx.coroutines.awaitCancellation
+import kotlinx.coroutines.coroutineScope
+import kotlinx.coroutines.launch
-@OptIn(ExperimentalCoroutinesApi::class)
-@SysUISingleton
class QuickQuickSettingsViewModel
-@Inject
+@AssistedInject
constructor(
tilesInteractor: CurrentTilesInteractor,
- qsColumnsViewModel: QSColumnsViewModel,
+ private val qsColumnsViewModel: QSColumnsViewModel,
quickQuickSettingsRowInteractor: QuickQuickSettingsRowInteractor,
val squishinessViewModel: TileSquishinessViewModel,
- private val iconTilesViewModel: IconTilesViewModel,
- @Application private val applicationScope: CoroutineScope,
+ iconTilesViewModel: IconTilesViewModel,
val tileHapticsViewModelFactoryProvider: TileHapticsViewModelFactoryProvider,
-) {
+) : ExclusiveActivatable() {
- val columns = qsColumnsViewModel.columns
+ private val hydrator = Hydrator("QuickQuickSettingsViewModel")
- private val rows =
- quickQuickSettingsRowInteractor.rows.stateIn(
- applicationScope,
- SharingStarted.WhileSubscribed(),
- quickQuickSettingsRowInteractor.defaultRows,
+ val columns by qsColumnsViewModel.columns
+
+ private val largeTiles by
+ hydrator.hydratedStateOf(traceName = "largeTiles", source = iconTilesViewModel.largeTiles)
+
+ private val rows by
+ hydrator.hydratedStateOf(
+ traceName = "rows",
+ initialValue = quickQuickSettingsRowInteractor.defaultRows,
+ source = quickQuickSettingsRowInteractor.rows,
)
- val tileViewModels: StateFlow<List<SizedTile<TileViewModel>>> =
- columns
- .flatMapLatest { columns ->
- tilesInteractor.currentTiles.combine(rows, ::Pair).mapLatest { (tiles, rows) ->
- tiles
- .map { SizedTileImpl(TileViewModel(it.tile, it.spec), it.spec.width) }
- .let { splitInRowsSequence(it, columns).take(rows).toList().flatten() }
- }
- }
- .stateIn(
- applicationScope,
- SharingStarted.WhileSubscribed(),
- tilesInteractor.currentTiles.value
- .map { SizedTileImpl(TileViewModel(it.tile, it.spec), it.spec.width) }
- .let {
- splitInRowsSequence(it, columns.value).take(rows.value).toList().flatten()
- },
- )
+ private val currentTiles by
+ hydrator.hydratedStateOf(traceName = "currentTiles", source = tilesInteractor.currentTiles)
+
+ val tileViewModels by derivedStateOf {
+ currentTiles
+ .map { SizedTileImpl(TileViewModel(it.tile, it.spec), it.spec.width) }
+ .let { splitInRowsSequence(it, columns).take(rows).toList().flatten() }
+ }
private val TileSpec.width: Int
- get() = if (iconTilesViewModel.isIconTile(this)) 1 else 2
+ get() = if (largeTiles.contains(this)) 2 else 1
+
+ override suspend fun onActivated(): Nothing {
+ coroutineScope {
+ launch { hydrator.activate() }
+ launch { qsColumnsViewModel.activate() }
+ awaitCancellation()
+ }
+ }
+
+ @AssistedFactory
+ interface Factory {
+ fun create(): QuickQuickSettingsViewModel
+ }
}
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 b1eb3bb38728..da175c9120ba 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
@@ -24,22 +24,31 @@ import com.android.systemui.qs.panels.ui.viewmodel.TileGridViewModel
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
+import kotlinx.coroutines.awaitCancellation
+import kotlinx.coroutines.coroutineScope
+import kotlinx.coroutines.launch
class QuickSettingsContainerViewModel
@AssistedInject
constructor(
brightnessSliderViewModelFactory: BrightnessSliderViewModel.Factory,
+ quickQuickSettingsViewModelFactory: QuickQuickSettingsViewModel.Factory,
@Assisted supportsBrightnessMirroring: Boolean,
val tileGridViewModel: TileGridViewModel,
val editModeViewModel: EditModeViewModel,
- val quickQuickSettingsViewModel: QuickQuickSettingsViewModel,
) : ExclusiveActivatable() {
val brightnessSliderViewModel =
brightnessSliderViewModelFactory.create(supportsBrightnessMirroring)
+ val quickQuickSettingsViewModel = quickQuickSettingsViewModelFactory.create()
+
override suspend fun onActivated(): Nothing {
- brightnessSliderViewModel.activate()
+ coroutineScope {
+ launch { brightnessSliderViewModel.activate() }
+ launch { quickQuickSettingsViewModel.activate() }
+ awaitCancellation()
+ }
}
@AssistedFactory
diff --git a/packages/SystemUI/src/com/android/systemui/smartspace/config/BcSmartspaceConfigProvider.kt b/packages/SystemUI/src/com/android/systemui/smartspace/config/BcSmartspaceConfigProvider.kt
index 5db1dcbb6c2c..f741b85cd1e0 100644
--- a/packages/SystemUI/src/com/android/systemui/smartspace/config/BcSmartspaceConfigProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/smartspace/config/BcSmartspaceConfigProvider.kt
@@ -16,7 +16,7 @@
package com.android.systemui.smartspace.config
-import com.android.systemui.Flags.smartspaceSwipeEventLogging
+import com.android.systemui.Flags.smartspaceSwipeEventLoggingFix
import com.android.systemui.Flags.smartspaceViewpager2
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.plugins.BcSmartspaceConfigPlugin
@@ -30,5 +30,5 @@ class BcSmartspaceConfigProvider(private val featureFlags: FeatureFlags) :
get() = smartspaceViewpager2()
override val isSwipeEventLoggingEnabled: Boolean
- get() = smartspaceSwipeEventLogging()
+ get() = smartspaceSwipeEventLoggingFix()
}
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 57c8bc6133e6..614f0f48d1fc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/core/CommandQueueInitializer.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/core/CommandQueueInitializer.kt
@@ -46,7 +46,7 @@ constructor(
) : CoreStartable {
override fun start() {
- StatusBarSimpleFragment.assertInNewMode()
+ StatusBarConnectedDisplays.assertInNewMode()
val result: RegisterStatusBarResult =
try {
barService.registerStatusBar(commandQueue)
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 aa2a08f980f9..08d177f933c1 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
@@ -1987,6 +1987,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
mColorUpdateLogger = colorUpdateLogger;
mDismissibilityProvider = dismissibilityProvider;
mFeatureFlags = featureFlags;
+ setHapticFeedbackEnabled(!com.android.systemui.Flags.msdlFeedback());
}
private void initDimens() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
index 23a2facf4c5a..baad616cbf02 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
@@ -64,6 +64,9 @@ import com.android.systemui.statusbar.policy.dagger.RemoteInputViewSubcomponent;
import com.android.systemui.util.time.SystemClock;
import com.android.systemui.wmshell.BubblesManager;
+import com.google.android.msdl.data.model.MSDLToken;
+import com.google.android.msdl.domain.MSDLPlayer;
+
import java.util.List;
import java.util.Optional;
@@ -114,6 +117,7 @@ public class ExpandableNotificationRowController implements NotifViewController
private final NotificationDismissibilityProvider mDismissibilityProvider;
private final IStatusBarService mStatusBarService;
private final UiEventLogger mUiEventLogger;
+ private final MSDLPlayer mMSDLPlayer;
private final NotificationSettingsController mSettingsController;
@@ -273,7 +277,8 @@ public class ExpandableNotificationRowController implements NotifViewController
ExpandableNotificationRowDragController dragController,
NotificationDismissibilityProvider dismissibilityProvider,
IStatusBarService statusBarService,
- UiEventLogger uiEventLogger) {
+ UiEventLogger uiEventLogger,
+ MSDLPlayer msdlPlayer) {
mView = view;
mListContainer = listContainer;
mRemoteInputViewSubcomponentFactory = rivSubcomponentFactory;
@@ -309,6 +314,7 @@ public class ExpandableNotificationRowController implements NotifViewController
mDismissibilityProvider = dismissibilityProvider;
mStatusBarService = statusBarService;
mUiEventLogger = uiEventLogger;
+ mMSDLPlayer = msdlPlayer;
}
/**
@@ -352,6 +358,9 @@ public class ExpandableNotificationRowController implements NotifViewController
}
mView.setLongPressListener((v, x, y, item) -> {
+ if (com.android.systemui.Flags.msdlFeedback()) {
+ mMSDLPlayer.playToken(MSDLToken.LONG_PRESS, null);
+ }
if (mView.isSummaryWithChildren()) {
mView.expandNotification();
return true;
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 c1d72e4f7a2a..57af8ea19722 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
@@ -678,7 +678,9 @@ public class NotificationStackScrollLayout
mGroupMembershipManager = Dependency.get(GroupMembershipManager.class);
mGroupExpansionManager = Dependency.get(GroupExpansionManager.class);
setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
- setWindowInsetsAnimationCallback(mInsetsCallback);
+ if (!SceneContainerFlag.isEnabled()) {
+ setWindowInsetsAnimationCallback(mInsetsCallback);
+ }
}
/**
@@ -795,7 +797,7 @@ public class NotificationStackScrollLayout
&& !onKeyguard()
&& mUpcomingStatusBarState != StatusBarState.KEYGUARD
// quick settings don't affect notifications when not in full screen
- && (mQsExpansionFraction != 1 || !mQsFullScreen)
+ && (getQsExpansionFraction() != 1 || !mQsFullScreen)
&& !mScreenOffAnimationController.shouldHideNotificationsFooter()
&& !mIsRemoteInputActive;
}
@@ -1526,7 +1528,7 @@ public class NotificationStackScrollLayout
float fraction = mAmbientState.getExpansionFraction();
// If we are on quick settings, we need to quickly hide it to show the bouncer to avoid an
// overlap. Otherwise, we maintain the normal fraction for smoothness.
- if (mAmbientState.isBouncerInTransit() && mQsExpansionFraction > 0f) {
+ if (mAmbientState.isBouncerInTransit() && getQsExpansionFraction() > 0f) {
fraction = BouncerPanelExpansionCalculator.aboutToShowBouncerProgress(fraction);
}
final float stackY = MathUtils.lerp(0, endTopPosition, fraction);
@@ -1550,7 +1552,7 @@ public class NotificationStackScrollLayout
}
updateInterpolatedStackHeight(endHeight, fraction);
} else {
- if (mQsExpansionFraction <= 0 && !shouldSkipHeightUpdate()) {
+ if (getQsExpansionFraction() <= 0 && !shouldSkipHeightUpdate()) {
final float endHeight = updateStackEndHeight(
getHeight(), getEmptyBottomMarginInternal(), getTopPadding());
updateInterpolatedStackHeight(endHeight, fraction);
@@ -1694,7 +1696,7 @@ public class NotificationStackScrollLayout
} else if (mQsFullScreen) {
int stackStartPosition =
getContentHeight() - getTopPadding() + getIntrinsicPadding();
- int stackEndPosition = mMaxTopPadding + mShelf.getIntrinsicHeight();
+ int stackEndPosition = getMaxTopPadding() + mShelf.getIntrinsicHeight();
if (stackStartPosition <= stackEndPosition) {
stackHeight = stackEndPosition;
} else {
@@ -1703,7 +1705,7 @@ public class NotificationStackScrollLayout
stackHeight = (int) height;
} else {
stackHeight = (int) NotificationUtils.interpolate(stackStartPosition,
- stackEndPosition, mQsExpansionFraction);
+ stackEndPosition, getQsExpansionFraction());
}
}
} else {
@@ -2086,6 +2088,7 @@ public class NotificationStackScrollLayout
}
private void updateImeInset(WindowInsets windowInsets) {
+ SceneContainerFlag.assertInLegacyMode();
mImeInset = windowInsets.getInsets(WindowInsets.Type.ime()).bottom;
if (mFooterView != null && mFooterView.getViewState() != null) {
@@ -2112,7 +2115,7 @@ public class NotificationStackScrollLayout
if (cutout != null) {
mWaterfallTopInset = cutout.getWaterfallInsets().top;
}
- if (!mIsInsetAnimationRunning) {
+ if (!SceneContainerFlag.isEnabled() && !mIsInsetAnimationRunning) {
// update bottom inset e.g. after rotation
updateImeInset(insets);
}
@@ -2513,6 +2516,7 @@ public class NotificationStackScrollLayout
}
private int getImeInset() {
+ SceneContainerFlag.assertInLegacyMode();
// The NotificationStackScrollLayout does not extend all the way to the bottom of the
// display. Therefore, subtract that space from the mImeInset, in order to only include
// the portion of the bottom inset that actually overlaps the NotificationStackScrollLayout.
@@ -2851,11 +2855,6 @@ public class NotificationStackScrollLayout
setExpandedHeight(mExpandedHeight);
}
- public void setMaxTopPadding(int maxTopPadding) {
- SceneContainerFlag.assertInLegacyMode();
- mMaxTopPadding = maxTopPadding;
- }
-
public int getLayoutMinHeight() {
SceneContainerFlag.assertInLegacyMode();
return getLayoutMinHeightInternal();
@@ -3639,7 +3638,11 @@ public class NotificationStackScrollLayout
* @return Whether a y coordinate is inside the content.
*/
public boolean isInContentBounds(float y) {
- return y < getHeight() - getEmptyBottomMarginInternal();
+ if (SceneContainerFlag.isEnabled()) {
+ return y < mAmbientState.getStackCutoff();
+ } else {
+ return y < getHeight() - getEmptyBottomMarginInternal();
+ }
}
private float getTouchSlop(MotionEvent event) {
@@ -5268,17 +5271,22 @@ public class NotificationStackScrollLayout
return mQsFullScreen;
}
+ private float getQsExpansionFraction() {
+ SceneContainerFlag.assertInLegacyMode();
+ return mQsExpansionFraction;
+ }
+
public void setQsExpansionFraction(float qsExpansionFraction) {
SceneContainerFlag.assertInLegacyMode();
- boolean footerAffected = mQsExpansionFraction != qsExpansionFraction
- && (mQsExpansionFraction == 1 || qsExpansionFraction == 1);
+ boolean footerAffected = getQsExpansionFraction() != qsExpansionFraction
+ && (getQsExpansionFraction() == 1 || qsExpansionFraction == 1);
mQsExpansionFraction = qsExpansionFraction;
updateUseRoundedRectClipping();
// If notifications are scrolled,
// clear out scrollY by the time we push notifications offscreen
if (getOwnScrollY() > 0) {
- setOwnScrollY((int) MathUtils.lerp(getOwnScrollY(), 0, mQsExpansionFraction));
+ setOwnScrollY((int) MathUtils.lerp(getOwnScrollY(), 0, getQsExpansionFraction()));
}
if (!FooterViewRefactor.isEnabled() && footerAffected) {
updateFooter();
@@ -5510,9 +5518,7 @@ public class NotificationStackScrollLayout
println(pw, "alpha", getAlpha());
println(pw, "suppressChildrenMeasureLayout", mSuppressChildrenMeasureAndLayout);
println(pw, "scrollY", mAmbientState.getScrollY());
- println(pw, "maxTopPadding", mMaxTopPadding);
println(pw, "showShelfOnly", mShouldShowShelfOnly);
- println(pw, "qsExpandFraction", mQsExpansionFraction);
println(pw, "isCurrentUserSetup", mIsCurrentUserSetup);
println(pw, "hideAmount", mAmbientState.getHideAmount());
println(pw, "ambientStateSwipingUp", mAmbientState.isSwipingUp());
@@ -5547,6 +5553,8 @@ public class NotificationStackScrollLayout
println(pw, "intrinsicContentHeight", getIntrinsicContentHeight());
println(pw, "contentHeight", getContentHeight());
println(pw, "topPadding", getTopPadding());
+ println(pw, "maxTopPadding", getMaxTopPadding());
+ println(pw, "qsExpandFraction", getQsExpansionFraction());
}
});
pw.println();
@@ -5619,7 +5627,9 @@ public class NotificationStackScrollLayout
pw.println("mIsCurrentUserSetup: " + mIsCurrentUserSetup);
pw.println("onKeyguard: " + onKeyguard());
pw.println("mUpcomingStatusBarState: " + mUpcomingStatusBarState);
- pw.println("mQsExpansionFraction: " + mQsExpansionFraction);
+ if (!SceneContainerFlag.isEnabled()) {
+ pw.println("QsExpansionFraction: " + getQsExpansionFraction());
+ }
pw.println("mQsFullScreen: " + mQsFullScreen);
pw.println(
"mScreenOffAnimationController"
@@ -6243,7 +6253,8 @@ public class NotificationStackScrollLayout
if (SceneContainerFlag.isEnabled()) return;
// We don't want to clip notifications when QS is expanded, because incoming heads up on
// the bottom would be clipped otherwise
- boolean qsAllowsClipping = mQsExpansionFraction < 0.5f || mShouldUseSplitNotificationShade;
+ boolean qsAllowsClipping =
+ getQsExpansionFraction() < 0.5f || mShouldUseSplitNotificationShade;
boolean clip = mIsExpanded && qsAllowsClipping;
if (clip != mShouldUseRoundedRectClipping) {
mShouldUseRoundedRectClipping = clip;
@@ -6953,10 +6964,12 @@ public class NotificationStackScrollLayout
/** Use {@link ScrollViewFields#intrinsicStackHeight}, when SceneContainerFlag is enabled. */
private int getContentHeight() {
+ SceneContainerFlag.assertInLegacyMode();
return mContentHeight;
}
private void setContentHeight(int contentHeight) {
+ SceneContainerFlag.assertInLegacyMode();
mContentHeight = contentHeight;
}
@@ -6965,10 +6978,23 @@ public class NotificationStackScrollLayout
* @return the height of the content ignoring the footer.
*/
public float getIntrinsicContentHeight() {
+ SceneContainerFlag.assertInLegacyMode();
return mIntrinsicContentHeight;
}
private void setIntrinsicContentHeight(float intrinsicContentHeight) {
+ SceneContainerFlag.assertInLegacyMode();
mIntrinsicContentHeight = intrinsicContentHeight;
}
+
+ private int getMaxTopPadding() {
+ SceneContainerFlag.assertInLegacyMode();
+ return mMaxTopPadding;
+ }
+
+ /** Not used with SceneContainerFlag, because we rely on the placeholder for placement. */
+ public void setMaxTopPadding(int maxTopPadding) {
+ SceneContainerFlag.assertInLegacyMode();
+ mMaxTopPadding = maxTopPadding;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt
index 96d0d766d780..827e2bfeba0f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt
@@ -228,9 +228,14 @@ constructor(
configurationInteractor.onAnyConfigurationChange,
) { isShadeLayoutWide, shadeMode, _ ->
with(context.resources) {
- // TODO(b/338033836): Define separate horizontal margins for dual shade.
val marginHorizontal =
- getDimensionPixelSize(R.dimen.notification_panel_margin_horizontal)
+ getDimensionPixelSize(
+ if (shadeMode is Dual) {
+ R.dimen.shade_panel_margin_horizontal
+ } else {
+ R.dimen.notification_panel_margin_horizontal
+ }
+ )
val horizontalPosition =
when (shadeMode) {
@@ -248,10 +253,7 @@ constructor(
ConfigurationBasedDimensions(
horizontalPosition = horizontalPosition,
- marginStart =
- if (horizontalPosition is HorizontalPosition.EdgeToEdge)
- marginHorizontal
- else 0,
+ marginStart = if (shadeMode is Split) 0 else marginHorizontal,
marginEnd = marginHorizontal,
marginBottom =
getDimensionPixelSize(R.dimen.notification_panel_margin_bottom),
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 9cda199d5279..0c511aeae3e5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -1348,6 +1348,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
if (!canHandleBackPressed()) {
return;
}
+ mStatusBarStateController.setLeaveOpenOnKeyguardHide(false);
boolean hideBouncerOverDream = isBouncerShowing()
&& mDreamOverlayStateController.isOverlayActive();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java
index a6581159d33e..d2c2003112f6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java
@@ -37,6 +37,7 @@ import com.android.systemui.Dumpable;
import com.android.systemui.ScreenDecorations;
import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor;
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor;
+import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.res.R;
import com.android.systemui.scene.domain.interactor.SceneInteractor;
@@ -78,6 +79,7 @@ public final class StatusBarTouchableRegionManager implements Dumpable {
private View mNotificationShadeWindowView;
private View mNotificationPanelView;
private boolean mForceCollapsedUntilLayout = false;
+ private Boolean mCommunalVisible = false;
private Region mTouchableRegion = new Region();
private int mDisplayCutoutTouchableRegionSize;
@@ -98,7 +100,8 @@ public final class StatusBarTouchableRegionManager implements Dumpable {
JavaAdapter javaAdapter,
UnlockedScreenOffAnimationController unlockedScreenOffAnimationController,
PrimaryBouncerInteractor primaryBouncerInteractor,
- AlternateBouncerInteractor alternateBouncerInteractor
+ AlternateBouncerInteractor alternateBouncerInteractor,
+ CommunalSceneInteractor communalSceneInteractor
) {
mContext = context;
initResources();
@@ -145,6 +148,9 @@ public final class StatusBarTouchableRegionManager implements Dumpable {
javaAdapter.alwaysCollectFlow(
shadeInteractor.isAnyExpanded(),
this::onShadeOrQsExpanded);
+ javaAdapter.alwaysCollectFlow(
+ communalSceneInteractor.isCommunalVisible(),
+ this::onCommunalVisible);
}
mPrimaryBouncerInteractor = primaryBouncerInteractor;
@@ -196,6 +202,10 @@ public final class StatusBarTouchableRegionManager implements Dumpable {
}
}
+ private void onCommunalVisible(Boolean visible) {
+ mCommunalVisible = visible;
+ }
+
/**
* Calculates the touch region needed for heads up notifications, taking into consideration
* any existing display cutouts (notch)
@@ -304,6 +314,9 @@ public final class StatusBarTouchableRegionManager implements Dumpable {
&& (!mIsSceneContainerUiEmpty || mIsRemoteUserInteractionOngoing))
|| mPrimaryBouncerInteractor.isShowing().getValue()
|| mAlternateBouncerInteractor.isVisibleState()
+ // The glanceable hub is a full-screen UI within the notification shade window. When
+ // it's visible, the touchable region should be the full screen.
+ || mCommunalVisible
|| mUnlockedScreenOffAnimationController.isAnimationPlaying();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.kt
index e4a75beca9f9..23f3482d40bd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.kt
@@ -143,7 +143,7 @@ interface StatusBarPhoneModule {
fun commandQueueInitializerCoreStartable(
initializerLazy: Lazy<CommandQueueInitializer>
): CoreStartable {
- return if (StatusBarSimpleFragment.isEnabled) {
+ return if (StatusBarConnectedDisplays.isEnabled) {
initializerLazy.get()
} else {
CoreStartable.NOP
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
index 48a3ada8bc5f..e1b8a1d9971c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
@@ -22,7 +22,6 @@ import android.content.res.Configuration
import android.hardware.biometrics.BiometricAuthenticator
import android.hardware.biometrics.BiometricConstants
import android.hardware.biometrics.BiometricManager
-import android.hardware.biometrics.Flags.FLAG_CUSTOM_BIOMETRIC_PROMPT
import android.hardware.biometrics.PromptContentViewWithMoreOptionsButton
import android.hardware.biometrics.PromptInfo
import android.hardware.biometrics.PromptVerticalListContentView
@@ -413,7 +412,6 @@ open class AuthContainerViewTest : SysuiTestCase() {
@Test
fun testShowBiometricUI_ContentViewWithMoreOptionsButton() {
- mSetFlagsRule.enableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT)
var isButtonClicked = false
val contentView =
PromptContentViewWithMoreOptionsButton.Builder()
@@ -450,7 +448,6 @@ open class AuthContainerViewTest : SysuiTestCase() {
@Test
fun testShowCredentialUI_withVerticalListContentView() {
- mSetFlagsRule.enableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT)
val container =
initializeFingerprintContainer(
authenticators = BiometricManager.Authenticators.DEVICE_CREDENTIAL,
@@ -471,7 +468,6 @@ open class AuthContainerViewTest : SysuiTestCase() {
@Test
fun testShowCredentialUI_withContentViewWithMoreOptionsButton() {
- mSetFlagsRule.enableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT)
val contentView =
PromptContentViewWithMoreOptionsButton.Builder()
.setMoreOptionsButtonListener(fakeExecutor) { _, _ -> }
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 06e8b1cf8dab..6069b4409ec9 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
@@ -610,6 +610,7 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
@Test
@DisableFlags({FooterViewRefactor.FLAG_NAME, NotifRedesignFooter.FLAG_NAME})
+ @DisableSceneContainer
public void testUpdateFooter_remoteInput() {
setBarStateForTest(StatusBarState.SHADE);
mStackScroller.setCurrentUserSetup(true);
@@ -643,6 +644,7 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
@Test
@DisableFlags({FooterViewRefactor.FLAG_NAME, NotifRedesignFooter.FLAG_NAME})
+ @DisableSceneContainer
public void testUpdateFooter_oneClearableNotification() {
setBarStateForTest(StatusBarState.SHADE);
mStackScroller.setCurrentUserSetup(true);
@@ -659,6 +661,7 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
@Test
@DisableFlags({FooterViewRefactor.FLAG_NAME, NotifRedesignFooter.FLAG_NAME})
+ @DisableSceneContainer
public void testUpdateFooter_withoutHistory() {
setBarStateForTest(StatusBarState.SHADE);
mStackScroller.setCurrentUserSetup(true);
@@ -692,6 +695,7 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
@Test
@DisableFlags({FooterViewRefactor.FLAG_NAME, NotifRedesignFooter.FLAG_NAME})
+ @DisableSceneContainer
public void testUpdateFooter_oneNonClearableNotification() {
setBarStateForTest(StatusBarState.SHADE);
mStackScroller.setCurrentUserSetup(true);
@@ -1207,6 +1211,7 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
}
@Test
+ @DisableSceneContainer
public void testWindowInsetAnimationProgress_updatesBottomInset() {
int imeInset = 100;
WindowInsets windowInsets = new WindowInsets.Builder()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
index c4c2aa7934f2..48106de5225b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -2533,6 +2533,42 @@ public class BubblesTest extends SysuiTestCase {
verify(mBubbleLogger).log(BubbleLogger.Event.BUBBLE_BAR_DISMISSED_DRAG_BAR);
}
+ @EnableFlags(FLAG_ENABLE_BUBBLE_BAR)
+ @Test
+ public void testEventLogging_bubbleBar_expandAndCollapse() {
+ mBubbleProperties.mIsBubbleBarEnabled = true;
+ mPositioner.setIsLargeScreen(true);
+ FakeBubbleStateListener bubbleStateListener = new FakeBubbleStateListener();
+ mBubbleController.registerBubbleStateListener(bubbleStateListener);
+
+ mEntryListener.onEntryAdded(mRow);
+ mBubbleController.expandStackAndSelectBubbleFromLauncher(mRow.getKey(), 0);
+
+ verify(mBubbleLogger).log(eqBubbleWithKey(mRow.getKey()),
+ eq(BubbleLogger.Event.BUBBLE_BAR_EXPANDED));
+
+ mBubbleController.collapseStack();
+
+ verify(mBubbleLogger).log(eqBubbleWithKey(mRow.getKey()),
+ eq(BubbleLogger.Event.BUBBLE_BAR_COLLAPSED));
+ }
+
+ @EnableFlags(FLAG_ENABLE_BUBBLE_BAR)
+ @Test
+ public void testEventLogging_bubbleBar_autoExpandingBubble() {
+ mBubbleProperties.mIsBubbleBarEnabled = true;
+ mPositioner.setIsLargeScreen(true);
+ FakeBubbleStateListener bubbleStateListener = new FakeBubbleStateListener();
+ mBubbleController.registerBubbleStateListener(bubbleStateListener);
+
+ setMetadataFlags(mRow,
+ Notification.BubbleMetadata.FLAG_AUTO_EXPAND_BUBBLE, true /* enableFlag */);
+ mEntryListener.onEntryAdded(mRow);
+
+ verify(mBubbleLogger).log(eqBubbleWithKey(mRow.getKey()),
+ eq(BubbleLogger.Event.BUBBLE_BAR_EXPANDED));
+ }
+
/** Creates a bubble using the userId and package. */
private Bubble createBubble(int userId, String pkg) {
final UserHandle userHandle = new UserHandle(userId);
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalWidgetRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalWidgetRepository.kt
index 62d221dd9790..b27dadc9035b 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalWidgetRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalWidgetRepository.kt
@@ -40,8 +40,18 @@ class FakeCommunalWidgetRepository(private val coroutineScope: CoroutineScope) :
) {
coroutineScope.launch {
val id = nextWidgetId++
- val providerInfo = AppWidgetProviderInfo().apply { this.provider = provider }
- val configured = configurator?.configureWidget(id) ?: true
+ val providerInfo = createAppWidgetProviderInfo(provider, user.identifier)
+
+ fakeDatabase[id] =
+ CommunalWidgetContentModel.Available(
+ appWidgetId = id,
+ rank = rank ?: 0,
+ providerInfo = providerInfo,
+ spanY = 3,
+ )
+ updateListFromDatabase()
+
+ val configured = configurator?.configureWidget(id) != false
if (configured) {
onConfigured(id, providerInfo, rank ?: -1)
}
@@ -61,20 +71,15 @@ class FakeCommunalWidgetRepository(private val coroutineScope: CoroutineScope) :
appWidgetId = appWidgetId,
rank = rank,
providerInfo =
- AppWidgetProviderInfo().apply {
- provider = ComponentName.unflattenFromString(componentName)!!
- widgetCategory = category
- providerInfo =
- ActivityInfo().apply {
- applicationInfo =
- ApplicationInfo().apply {
- uid = userId * UserHandle.PER_USER_RANGE
- }
- }
- },
+ createAppWidgetProviderInfo(
+ ComponentName.unflattenFromString(componentName)!!,
+ userId,
+ category,
+ ),
spanY = spanY,
)
updateListFromDatabase()
+ nextWidgetId = appWidgetId + 1
}
fun addPendingWidget(
@@ -151,4 +156,20 @@ class FakeCommunalWidgetRepository(private val coroutineScope: CoroutineScope) :
)
updateListFromDatabase()
}
+
+ private fun createAppWidgetProviderInfo(
+ componentName: ComponentName,
+ userId: Int,
+ category: Int = AppWidgetProviderInfo.WIDGET_CATEGORY_KEYGUARD,
+ ): AppWidgetProviderInfo {
+ return AppWidgetProviderInfo().apply {
+ provider = componentName
+ widgetCategory = category
+ providerInfo =
+ ActivityInfo().apply {
+ applicationInfo =
+ ApplicationInfo().apply { uid = userId * UserHandle.PER_USER_RANGE }
+ }
+ }
+ }
}
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 629fda6b610c..1f68195a9acc 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
@@ -54,7 +54,6 @@ val Kosmos.communalInteractor by Fixture {
keyguardInteractor = keyguardInteractor,
keyguardTransitionInteractor = keyguardTransitionInteractor,
communalSettingsInteractor = communalSettingsInteractor,
- appWidgetHost = mock(),
editWidgetsActivityStarter = editWidgetsActivityStarter,
userTracker = userTracker,
activityStarter = activityStarter,
@@ -62,7 +61,7 @@ val Kosmos.communalInteractor by Fixture {
sceneInteractor = sceneInteractor,
logBuffer = logcatLogBuffer("CommunalInteractor"),
tableLogBuffer = mock(),
- managedProfileController = fakeManagedProfileController
+ managedProfileController = fakeManagedProfileController,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/shared/model/FakeGlanceableHubMultiUserHelper.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/shared/model/FakeGlanceableHubMultiUserHelper.kt
new file mode 100644
index 000000000000..de44399b4a5b
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/shared/model/FakeGlanceableHubMultiUserHelper.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.communal.shared.model
+
+class FakeGlanceableHubMultiUserHelper(
+ override val glanceableHubHsumFlagEnabled: Boolean = true,
+ private var isHeadlessSystemUserMode: Boolean = false,
+ private var isInHeadlessSystemUser: Boolean = false,
+) : GlanceableHubMultiUserHelper {
+
+ override fun isHeadlessSystemUserMode(): Boolean {
+ return isHeadlessSystemUserMode
+ }
+
+ fun setIsHeadlessSystemUserMode(isHeadlessSystemUserMode: Boolean) {
+ this.isHeadlessSystemUserMode = isHeadlessSystemUserMode
+ }
+
+ override fun isInHeadlessSystemUser(): Boolean {
+ return isInHeadlessSystemUser
+ }
+
+ fun setIsInHeadlessSystemUser(isInHeadlessSystemUser: Boolean) {
+ this.isInHeadlessSystemUser = isInHeadlessSystemUser
+ }
+
+ override fun assertInHeadlessSystemUser() {
+ check(isInHeadlessSystemUser())
+ }
+
+ override fun assertNotInHeadlessSystemUser() {
+ check(!isInHeadlessSystemUser())
+ }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/shared/model/GlanceableHubMultiUserHelperKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/shared/model/GlanceableHubMultiUserHelperKosmos.kt
new file mode 100644
index 000000000000..adee44074e51
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/shared/model/GlanceableHubMultiUserHelperKosmos.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.communal.shared.model
+
+import com.android.systemui.kosmos.Kosmos
+
+val Kosmos.fakeGlanceableHubMultiUserHelper by Kosmos.Fixture { FakeGlanceableHubMultiUserHelper() }
+
+val Kosmos.glanceableHubMultiUserHelper by
+ Kosmos.Fixture<GlanceableHubMultiUserHelper> { fakeGlanceableHubMultiUserHelper }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/widgets/GlanceableHubWidgetManagerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/widgets/GlanceableHubWidgetManagerKosmos.kt
new file mode 100644
index 000000000000..583ae443c95f
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/widgets/GlanceableHubWidgetManagerKosmos.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.communal.widgets
+
+import com.android.systemui.kosmos.Kosmos
+import org.mockito.kotlin.mock
+
+val Kosmos.mockGlanceableHubWidgetManager by Kosmos.Fixture<GlanceableHubWidgetManager> { mock() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt
index 4976cc26068a..19e077c57de0 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt
@@ -53,6 +53,15 @@ class FakeKeyguardTransitionRepository(
private val initInLockscreen: Boolean = true,
/**
+ * Initial value for [FakeKeyguardTransitionRepository.sendTransitionStepsOnStartTransition].
+ * This needs to be configurable in the constructor since some transitions are triggered on
+ * init, before a test has the chance to set sendTransitionStepsOnStartTransition to false.
+ */
+ private val initiallySendTransitionStepsOnStartTransition: Boolean = true,
+ private val testScope: TestScope,
+) : KeyguardTransitionRepository {
+
+ /**
* If true, calls to [startTransition] will automatically emit STARTED, RUNNING, and FINISHED
* transition steps from/to the given states.
*
@@ -64,11 +73,9 @@ class FakeKeyguardTransitionRepository(
*
* If your test needs to make assertions at specific points between STARTED/FINISHED, or if it's
* difficult to set up all of the conditions to make the transition interactors actually call
- * startTransition, then construct a FakeKeyguardTransitionRepository with this value false.
+ * startTransition, set this value to false.
*/
- private val sendTransitionStepsOnStartTransition: Boolean = true,
- private val testScope: TestScope,
-) : KeyguardTransitionRepository {
+ var sendTransitionStepsOnStartTransition = initiallySendTransitionStepsOnStartTransition
private val _transitions =
MutableSharedFlow<TransitionStep>(replay = 3, onBufferOverflow = BufferOverflow.DROP_OLDEST)
@@ -77,7 +84,11 @@ class FakeKeyguardTransitionRepository(
@Inject
constructor(
testScope: TestScope
- ) : this(initInLockscreen = true, sendTransitionStepsOnStartTransition = true, testScope)
+ ) : this(
+ initInLockscreen = true,
+ initiallySendTransitionStepsOnStartTransition = true,
+ testScope
+ )
private val _currentTransitionInfo: MutableStateFlow<TransitionInfo> =
MutableStateFlow(
@@ -176,6 +187,20 @@ class FakeKeyguardTransitionRepository(
testScheduler: TestCoroutineScheduler,
throughTransitionState: TransitionState = TransitionState.FINISHED,
) {
+ val lastStep = _transitions.replayCache.lastOrNull()
+ if (lastStep != null && lastStep.transitionState != TransitionState.FINISHED) {
+ sendTransitionStep(
+ step =
+ TransitionStep(
+ transitionState = TransitionState.CANCELED,
+ from = lastStep.from,
+ to = lastStep.to,
+ value = 0f,
+ )
+ )
+ testScheduler.runCurrent()
+ }
+
sendTransitionStep(
step =
TransitionStep(
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataManagerImplKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataManagerImplKosmos.kt
new file mode 100644
index 000000000000..c337298fa8c8
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataManagerImplKosmos.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.media.controls.domain.pipeline
+
+import com.android.systemui.kosmos.Kosmos
+import org.mockito.kotlin.mock
+
+/** Empty mock */
+val Kosmos.legacyMediaDataManagerImpl by Kosmos.Fixture { mock<LegacyMediaDataManagerImpl>() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/MediaDataManagerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/MediaDataManagerKosmos.kt
new file mode 100644
index 000000000000..f733da12a95e
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/MediaDataManagerKosmos.kt
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.media.controls.domain.pipeline
+
+import com.android.systemui.kosmos.Kosmos
+
+var Kosmos.mediaDataManager: MediaDataManager by Kosmos.Fixture()
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerKosmos.kt
new file mode 100644
index 000000000000..3c35ce9bf003
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerKosmos.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.media.controls.ui.controller
+
+import com.android.systemui.kosmos.Kosmos
+import org.mockito.kotlin.mock
+
+/** Empty mock */
+val Kosmos.mediaCarouselController by Kosmos.Fixture { mock<MediaCarouselController>() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerLoggerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerLoggerKosmos.kt
new file mode 100644
index 000000000000..9f5a8328d403
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerLoggerKosmos.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.media.controls.ui.controller
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.log.core.FakeLogBuffer
+
+val Kosmos.mediaCarouselControllerLogger by
+ Kosmos.Fixture { MediaCarouselControllerLogger(FakeLogBuffer.Factory.create()) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManagerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManagerKosmos.kt
index 7c24b4cc3d60..624e174321ec 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManagerKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManagerKosmos.kt
@@ -16,8 +16,15 @@
package com.android.systemui.media.controls.ui.controller
+import android.content.testableContext
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
-import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.animation.UniqueObjectHostView
+import org.mockito.kotlin.any
+import org.mockito.kotlin.mock
-var Kosmos.mediaHierarchyManager by Fixture { mock<MediaHierarchyManager>() }
+var Kosmos.mediaHierarchyManager by Fixture {
+ mock<MediaHierarchyManager> {
+ on { register(any()) }.thenAnswer { UniqueObjectHostView(this@Fixture.testableContext) }
+ }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/ui/controller/MediaHostStatesManagerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/ui/controller/MediaHostStatesManagerKosmos.kt
new file mode 100644
index 000000000000..8b02c579d91d
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/ui/controller/MediaHostStatesManagerKosmos.kt
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.media.controls.ui.controller
+
+import com.android.systemui.kosmos.Kosmos
+
+val Kosmos.mediaHostStatesManager by Kosmos.Fixture { MediaHostStatesManager() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/ui/view/MediaHostKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/ui/view/MediaHostKosmos.kt
new file mode 100644
index 000000000000..9638e117362c
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/ui/view/MediaHostKosmos.kt
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.media.controls.ui.view
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.media.controls.domain.pipeline.mediaDataManager
+import com.android.systemui.media.controls.ui.controller.mediaCarouselController
+import com.android.systemui.media.controls.ui.controller.mediaCarouselControllerLogger
+import com.android.systemui.media.controls.ui.controller.mediaHierarchyManager
+import com.android.systemui.media.controls.ui.controller.mediaHostStatesManager
+import java.util.function.Supplier
+
+private val Kosmos.mediaHostProvider by
+ Kosmos.Fixture {
+ Supplier<MediaHost> {
+ MediaHost(
+ MediaHost.MediaHostStateHolder(),
+ mediaHierarchyManager,
+ mediaDataManager,
+ mediaHostStatesManager,
+ mediaCarouselController,
+ mediaCarouselControllerLogger,
+ )
+ }
+ }
+
+val Kosmos.qqsMediaHost by Kosmos.Fixture { mediaHostProvider.get() }
+val Kosmos.qsMediaHost by Kosmos.Fixture { mediaHostProvider.get() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/composefragment/dagger/QSFragmentComposeModuleKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/composefragment/dagger/QSFragmentComposeModuleKosmos.kt
new file mode 100644
index 000000000000..fb8ffb0c5eb2
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/composefragment/dagger/QSFragmentComposeModuleKosmos.kt
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.composefragment.dagger
+
+import com.android.systemui.kosmos.Kosmos
+
+var Kosmos.usingMediaInComposeFragment by Kosmos.Fixture<Boolean>()
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 462b4083aa44..bda3192085ed 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
@@ -22,10 +22,13 @@ import com.android.systemui.common.ui.domain.interactor.configurationInteractor
import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor
import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.media.controls.ui.view.qqsMediaHost
+import com.android.systemui.media.controls.ui.view.qsMediaHost
+import com.android.systemui.qs.composefragment.dagger.usingMediaInComposeFragment
import com.android.systemui.qs.footerActionsController
import com.android.systemui.qs.footerActionsViewModelFactory
import com.android.systemui.qs.panels.domain.interactor.tileSquishinessInteractor
-import com.android.systemui.qs.panels.ui.viewmodel.paginatedGridViewModel
+import com.android.systemui.qs.panels.ui.viewmodel.inFirstPageViewModel
import com.android.systemui.qs.ui.viewmodel.quickSettingsContainerViewModelFactory
import com.android.systemui.shade.largeScreenHeaderHelper
import com.android.systemui.shade.transition.largeScreenShadeInterpolator
@@ -53,7 +56,10 @@ val Kosmos.qsFragmentComposeViewModelFactory by
configurationInteractor,
largeScreenHeaderHelper,
tileSquishinessInteractor,
- paginatedGridViewModel,
+ inFirstPageViewModel,
+ qqsMediaHost,
+ qsMediaHost,
+ usingMediaInComposeFragment,
lifecycleScope,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/InFirstPageViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/InFirstPageViewModelKosmos.kt
new file mode 100644
index 000000000000..1fa74cabcdeb
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/InFirstPageViewModelKosmos.kt
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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
+
+import com.android.systemui.kosmos.Kosmos
+
+val Kosmos.inFirstPageViewModel by Kosmos.Fixture { InFirstPageViewModel() }
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 48ef57e9a62c..5c8ca83ff2ae 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
@@ -17,7 +17,6 @@
package com.android.systemui.qs.panels.ui.viewmodel
import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.qs.panels.domain.interactor.paginatedGridInteractor
val Kosmos.paginatedGridViewModel by
@@ -26,6 +25,6 @@ val Kosmos.paginatedGridViewModel by
iconTilesViewModel,
qsColumnsViewModel,
paginatedGridInteractor,
- applicationCoroutineScope,
+ inFirstPageViewModel,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/QuickQuickSettingsViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/QuickQuickSettingsViewModelKosmos.kt
index 41ee260fd5dd..20be5c675851 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/QuickQuickSettingsViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/QuickQuickSettingsViewModelKosmos.kt
@@ -18,19 +18,21 @@ package com.android.systemui.qs.panels.ui.viewmodel
import com.android.systemui.haptics.msdl.tileHapticsViewModelFactoryProvider
import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.qs.panels.domain.interactor.quickQuickSettingsRowInteractor
import com.android.systemui.qs.pipeline.domain.interactor.currentTilesInteractor
-val Kosmos.quickQuickSettingsViewModel by
+val Kosmos.quickQuickSettingsViewModelFactory by
Kosmos.Fixture {
- QuickQuickSettingsViewModel(
- currentTilesInteractor,
- qsColumnsViewModel,
- quickQuickSettingsRowInteractor,
- tileSquishinessViewModel,
- iconTilesViewModel,
- applicationCoroutineScope,
- tileHapticsViewModelFactoryProvider,
- )
+ object : QuickQuickSettingsViewModel.Factory {
+ override fun create(): QuickQuickSettingsViewModel {
+ return QuickQuickSettingsViewModel(
+ currentTilesInteractor,
+ qsColumnsViewModel,
+ quickQuickSettingsRowInteractor,
+ tileSquishinessViewModel,
+ iconTilesViewModel,
+ tileHapticsViewModelFactoryProvider,
+ )
+ }
+ }
}
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 6ded7517f1d2..ce103ec57a86 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
@@ -19,7 +19,7 @@ package com.android.systemui.qs.ui.viewmodel
import com.android.systemui.brightness.ui.viewmodel.brightnessSliderViewModelFactory
import com.android.systemui.kosmos.Kosmos
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.quickQuickSettingsViewModelFactory
import com.android.systemui.qs.panels.ui.viewmodel.tileGridViewModel
val Kosmos.quickSettingsContainerViewModelFactory by
@@ -30,10 +30,10 @@ val Kosmos.quickSettingsContainerViewModelFactory by
): QuickSettingsContainerViewModel {
return QuickSettingsContainerViewModel(
brightnessSliderViewModelFactory,
+ quickQuickSettingsViewModelFactory,
supportsBrightnessMirroring,
tileGridViewModel,
editModeViewModel,
- quickQuickSettingsViewModel,
)
}
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManagerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManagerKosmos.kt
index 8785256de452..87ea1473bba1 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManagerKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManagerKosmos.kt
@@ -19,6 +19,7 @@ package com.android.systemui.statusbar.phone
import android.content.applicationContext
import com.android.systemui.bouncer.domain.interactor.alternateBouncerInteractor
import com.android.systemui.bouncer.domain.interactor.primaryBouncerInteractor
+import com.android.systemui.communal.domain.interactor.communalSceneInteractor
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testScope
import com.android.systemui.scene.domain.interactor.sceneInteractor
@@ -43,5 +44,6 @@ var Kosmos.statusBarTouchableRegionManager by
mock<UnlockedScreenOffAnimationController>(),
primaryBouncerInteractor,
alternateBouncerInteractor,
+ communalSceneInteractor,
)
}
diff --git a/ravenwood/texts/ravenwood-annotation-allowed-classes.txt b/ravenwood/texts/ravenwood-annotation-allowed-classes.txt
index a26fe66da2ab..70c1d781587f 100644
--- a/ravenwood/texts/ravenwood-annotation-allowed-classes.txt
+++ b/ravenwood/texts/ravenwood-annotation-allowed-classes.txt
@@ -2,6 +2,9 @@
com.android.internal.ravenwood.*
+com.android.server.FgThread
+com.android.server.ServiceThread
+
com.android.internal.display.BrightnessSynchronizer
com.android.internal.util.ArrayUtils
com.android.internal.logging.MetricsLogger
diff --git a/services/Android.bp b/services/Android.bp
index f04c692c12d0..899e224c6fd7 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -188,6 +188,28 @@ art_profile_java_defaults {
},
}
+// Conditionally add crashrecovery stubs library
+soong_config_module_type {
+ name: "crashrecovery_java_defaults",
+ module_type: "java_defaults",
+ config_namespace: "ANDROID",
+ bool_variables: [
+ "release_crashrecovery_module",
+ ],
+ properties: [
+ "libs",
+ ],
+}
+
+crashrecovery_java_defaults {
+ name: "services_crashrecovery_stubs_conditionally",
+ soong_config_variables: {
+ release_crashrecovery_module: {
+ libs: ["service-crashrecovery.stubs.system_server"],
+ },
+ },
+}
+
// merge all required services into one jar
// ============================================================
soong_config_module_type {
@@ -213,6 +235,7 @@ system_java_library {
defaults: [
"services_java_defaults",
"art_profile_java_defaults",
+ "services_crashrecovery_stubs_conditionally",
],
installable: true,
diff --git a/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java b/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java
index 89f14b09d397..268e56487c4b 100644
--- a/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java
+++ b/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java
@@ -86,7 +86,6 @@ public class AppFunctionManagerServiceImpl extends IAppFunctionManager.Stub {
private final Context mContext;
private final Map<String, Object> mLocks = new WeakHashMap<>();
-
public AppFunctionManagerServiceImpl(@NonNull Context context) {
this(
context,
@@ -201,7 +200,7 @@ public class AppFunctionManagerServiceImpl extends IAppFunctionManager.Stub {
if (mCallerValidator.isUserOrganizationManaged(targetUser)) {
safeExecuteAppFunctionCallback.onResult(
ExecuteAppFunctionResponse.newFailure(
- ExecuteAppFunctionResponse.RESULT_INTERNAL_ERROR,
+ ExecuteAppFunctionResponse.RESULT_SYSTEM_ERROR,
"Cannot run on a device with a device owner or from the managed"
+ " profile.",
/* extras= */ null));
@@ -256,7 +255,7 @@ public class AppFunctionManagerServiceImpl extends IAppFunctionManager.Stub {
if (serviceIntent == null) {
safeExecuteAppFunctionCallback.onResult(
ExecuteAppFunctionResponse.newFailure(
- ExecuteAppFunctionResponse.RESULT_INTERNAL_ERROR,
+ ExecuteAppFunctionResponse.RESULT_SYSTEM_ERROR,
"Cannot find the target service.",
/* extras= */ null));
return;
@@ -449,7 +448,7 @@ public class AppFunctionManagerServiceImpl extends IAppFunctionManager.Stub {
Slog.e(TAG, "Failed to bind to the AppFunctionService");
safeExecuteAppFunctionCallback.onResult(
ExecuteAppFunctionResponse.newFailure(
- ExecuteAppFunctionResponse.RESULT_INTERNAL_ERROR,
+ ExecuteAppFunctionResponse.RESULT_SYSTEM_ERROR,
"Failed to bind the AppFunctionService.",
/* extras= */ null));
}
@@ -464,7 +463,7 @@ public class AppFunctionManagerServiceImpl extends IAppFunctionManager.Stub {
if (e instanceof CompletionException) {
e = e.getCause();
}
- int resultCode = ExecuteAppFunctionResponse.RESULT_INTERNAL_ERROR;
+ int resultCode = ExecuteAppFunctionResponse.RESULT_SYSTEM_ERROR;
if (e instanceof AppSearchException appSearchException) {
resultCode =
mapAppSearchResultFailureCodeToExecuteAppFunctionResponse(
@@ -486,13 +485,13 @@ public class AppFunctionManagerServiceImpl extends IAppFunctionManager.Stub {
switch (resultCode) {
case AppSearchResult.RESULT_NOT_FOUND:
- return ExecuteAppFunctionResponse.RESULT_INVALID_ARGUMENT;
+ return ExecuteAppFunctionResponse.RESULT_FUNCTION_NOT_FOUND;
case AppSearchResult.RESULT_INVALID_ARGUMENT:
case AppSearchResult.RESULT_INTERNAL_ERROR:
case AppSearchResult.RESULT_SECURITY_ERROR:
// fall-through
}
- return ExecuteAppFunctionResponse.RESULT_INTERNAL_ERROR;
+ return ExecuteAppFunctionResponse.RESULT_SYSTEM_ERROR;
}
private void registerAppSearchObserver(@NonNull TargetUser user) {
@@ -543,12 +542,13 @@ public class AppFunctionManagerServiceImpl extends IAppFunctionManager.Stub {
});
}
}
+
/**
* Retrieves the lock object associated with the given package name.
*
- * This method returns the lock object from the {@code mLocks} map if it exists.
- * If no lock is found for the given package name, a new lock object is created,
- * stored in the map, and returned.
+ * <p>This method returns the lock object from the {@code mLocks} map if it exists. If no lock
+ * is found for the given package name, a new lock object is created, stored in the map, and
+ * returned.
*/
@VisibleForTesting
@NonNull
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index f9abd8558ca8..68ff9725ce5c 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -1469,9 +1469,7 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
mSecurityPolicy.enforceCallFromPackage(callingPackage);
// Check that if a cross-profile binding is attempted, it is allowed.
- // Cross-profile binding is also allowed if the caller has interact across users permission.
- if (!mSecurityPolicy.isEnabledGroupProfile(providerProfileId)
- && !mSecurityPolicy.hasCallerInteractAcrossUsersPermission()) {
+ if (!mSecurityPolicy.isEnabledGroupProfile(providerProfileId)) {
return false;
}
@@ -2440,10 +2438,8 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
Slog.i(TAG, "getInstalledProvidersForProfiles() " + userId);
}
- // Ensure the profile is in the group and enabled, or that the caller has permission to
- // interact across users.
- if (!mSecurityPolicy.isEnabledGroupProfile(profileId)
- && !mSecurityPolicy.hasCallerInteractAcrossUsersPermission()) {
+ // Ensure the profile is in the group and enabled.
+ if (!mSecurityPolicy.isEnabledGroupProfile(profileId)) {
return null;
}
@@ -5235,11 +5231,14 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
return true;
}
final int userId = UserHandle.getUserId(uid);
- if ((widget.host.getUserId() == userId || (widget.provider != null
- && widget.provider.getUserId() == userId))
+ if ((widget.host.getUserId() == userId
+ || (widget.provider != null && widget.provider.getUserId() == userId)
+ || hasCallerInteractAcrossUsersPermission())
&& callerHasPermission(android.Manifest.permission.BIND_APPWIDGET)) {
- // Apps that run in the same user as either the host or the provider and
- // have the bind widget permission have access to the widget.
+ // Access to the widget requires the app to:
+ // - Run in the same user as the host or provider, or have permission to interact
+ // across users
+ // - Have bind widget permission
return true;
}
if (DEBUG) {
@@ -5260,16 +5259,12 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
* The provider is accessible by the caller if any of the following is true:
* - The provider belongs to the caller
* - The provider belongs to a profile of the caller and is allowlisted
- * - The caller has permission to interact across users
*/
public boolean canAccessProvider(String packageName, int profileId) {
final int callerId = UserHandle.getCallingUserId();
if (profileId == callerId) {
return true;
}
- if (hasCallerInteractAcrossUsersPermission()) {
- return true;
- }
final int parentId = getProfileParent(profileId);
if (parentId != callerId) {
return false;
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index a960015ecba6..51034d24df14 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -31,13 +31,16 @@ import static android.os.UserHandle.getCallingUserId;
import static com.android.internal.util.CollectionUtils.any;
import static com.android.internal.util.Preconditions.checkState;
+import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
import static com.android.server.companion.utils.PackageUtils.enforceUsesCompanionDeviceFeature;
+import static com.android.server.companion.utils.PackageUtils.getPackageInfo;
import static com.android.server.companion.utils.PackageUtils.isRestrictedSettingsAllowed;
import static com.android.server.companion.utils.PermissionsUtils.enforceCallerCanManageAssociationsForPackage;
import static com.android.server.companion.utils.PermissionsUtils.enforceCallerIsSystemOr;
import static com.android.server.companion.utils.PermissionsUtils.enforceCallerIsSystemOrCanInteractWithUserId;
import static java.util.Objects.requireNonNull;
+import static java.util.concurrent.TimeUnit.MINUTES;
import android.annotation.EnforcePermission;
import android.annotation.NonNull;
@@ -66,22 +69,31 @@ import android.companion.datatransfer.PermissionSyncRequest;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.content.SharedPreferences;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.net.MacAddress;
+import android.net.NetworkPolicyManager;
import android.os.Binder;
+import android.os.Environment;
import android.os.Parcel;
import android.os.ParcelFileDescriptor;
import android.os.PowerExemptionManager;
import android.os.PowerManagerInternal;
import android.os.RemoteException;
+import android.os.ServiceManager;
import android.os.UserHandle;
import android.os.UserManager;
import android.permission.flags.Flags;
+import android.util.ArraySet;
import android.util.ExceptionUtils;
import android.util.Slog;
+import com.android.internal.app.IAppOpsService;
import com.android.internal.content.PackageMonitor;
import com.android.internal.notification.NotificationAccessConfirmationActivityContract;
+import com.android.internal.os.BackgroundThread;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.DumpUtils;
import com.android.server.FgThread;
@@ -102,27 +114,35 @@ import com.android.server.companion.devicepresence.DevicePresenceProcessor;
import com.android.server.companion.devicepresence.ObservableUuid;
import com.android.server.companion.devicepresence.ObservableUuidStore;
import com.android.server.companion.transport.CompanionTransportManager;
+import com.android.server.pm.UserManagerInternal;
import com.android.server.wm.ActivityTaskManagerInternal;
+import java.io.File;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.Collection;
import java.util.List;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
+import java.util.Set;
@SuppressLint("LongLogTag")
public class CompanionDeviceManagerService extends SystemService {
private static final String TAG = "CDM_CompanionDeviceManagerService";
private static final long PAIR_WITHOUT_PROMPT_WINDOW_MS = 10 * 60 * 1000; // 10 min
+
+ private static final String PREF_FILE_NAME = "companion_device_preferences.xml";
+ private static final String PREF_KEY_AUTO_REVOKE_GRANTS_DONE = "auto_revoke_grants_done";
private static final int MAX_CN_LENGTH = 500;
+ private final ActivityTaskManagerInternal mAtmInternal;
+ private final ActivityManagerInternal mAmInternal;
+ private final IAppOpsService mAppOpsManager;
+ private final PowerExemptionManager mPowerExemptionManager;
+ private final PackageManagerInternal mPackageManagerInternal;
+
private final AssociationStore mAssociationStore;
private final SystemDataTransferRequestStore mSystemDataTransferRequestStore;
private final ObservableUuidStore mObservableUuidStore;
-
- private final CompanionExemptionProcessor mCompanionExemptionProcessor;
private final AssociationRequestsProcessor mAssociationRequestsProcessor;
private final SystemDataTransferProcessor mSystemDataTransferProcessor;
private final BackupRestoreProcessor mBackupRestoreProcessor;
@@ -136,15 +156,12 @@ public class CompanionDeviceManagerService extends SystemService {
super(context);
final ActivityManager activityManager = context.getSystemService(ActivityManager.class);
- final PowerExemptionManager powerExemptionManager = context.getSystemService(
- PowerExemptionManager.class);
- final AppOpsManager appOpsManager = context.getSystemService(AppOpsManager.class);
- final ActivityTaskManagerInternal atmInternal = LocalServices.getService(
- ActivityTaskManagerInternal.class);
- final ActivityManagerInternal amInternal = LocalServices.getService(
- ActivityManagerInternal.class);
- final PackageManagerInternal packageManagerInternal = LocalServices.getService(
- PackageManagerInternal.class);
+ mPowerExemptionManager = context.getSystemService(PowerExemptionManager.class);
+ mAppOpsManager = IAppOpsService.Stub.asInterface(
+ ServiceManager.getService(Context.APP_OPS_SERVICE));
+ mAtmInternal = LocalServices.getService(ActivityTaskManagerInternal.class);
+ mAmInternal = LocalServices.getService(ActivityManagerInternal.class);
+ mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
final UserManager userManager = context.getSystemService(UserManager.class);
final PowerManagerInternal powerManagerInternal = LocalServices.getService(
PowerManagerInternal.class);
@@ -156,29 +173,25 @@ public class CompanionDeviceManagerService extends SystemService {
// Init processors
mAssociationRequestsProcessor = new AssociationRequestsProcessor(context,
- packageManagerInternal, mAssociationStore);
- mBackupRestoreProcessor = new BackupRestoreProcessor(context, packageManagerInternal,
+ mPackageManagerInternal, mAssociationStore);
+ mBackupRestoreProcessor = new BackupRestoreProcessor(context, mPackageManagerInternal,
mAssociationStore, associationDiskStore, mSystemDataTransferRequestStore,
mAssociationRequestsProcessor);
mCompanionAppBinder = new CompanionAppBinder(context);
- mCompanionExemptionProcessor = new CompanionExemptionProcessor(context,
- powerExemptionManager, appOpsManager, packageManagerInternal, atmInternal,
- amInternal, mAssociationStore);
-
mDevicePresenceProcessor = new DevicePresenceProcessor(context,
mCompanionAppBinder, userManager, mAssociationStore, mObservableUuidStore,
- powerManagerInternal, mCompanionExemptionProcessor);
+ powerManagerInternal);
mTransportManager = new CompanionTransportManager(context, mAssociationStore);
mDisassociationProcessor = new DisassociationProcessor(context, activityManager,
- mAssociationStore, packageManagerInternal, mDevicePresenceProcessor,
+ mAssociationStore, mPackageManagerInternal, mDevicePresenceProcessor,
mCompanionAppBinder, mSystemDataTransferRequestStore, mTransportManager);
mSystemDataTransferProcessor = new SystemDataTransferProcessor(this,
- packageManagerInternal, mAssociationStore,
+ mPackageManagerInternal, mAssociationStore,
mSystemDataTransferRequestStore, mTransportManager);
// TODO(b/279663946): move context sync to a dedicated system service
@@ -189,6 +202,7 @@ public class CompanionDeviceManagerService extends SystemService {
public void onStart() {
// Init association stores
mAssociationStore.refreshCache();
+ mAssociationStore.registerLocalListener(mAssociationStoreChangeListener);
// Init UUID store
mObservableUuidStore.getObservableUuidsForUser(getContext().getUserId());
@@ -226,11 +240,11 @@ public class CompanionDeviceManagerService extends SystemService {
if (associations.isEmpty()) return;
- mCompanionExemptionProcessor.updateAtm(userId, associations);
+ updateAtm(userId, associations);
- try (ExecutorService executor = Executors.newSingleThreadExecutor()) {
- executor.execute(mCompanionExemptionProcessor::updateAutoRevokeExemptions);
- }
+ BackgroundThread.getHandler().sendMessageDelayed(
+ obtainMessage(CompanionDeviceManagerService::maybeGrantAutoRevokeExemptions, this),
+ MINUTES.toMillis(10));
}
@Override
@@ -248,12 +262,9 @@ public class CompanionDeviceManagerService extends SystemService {
if (!associationsForPackage.isEmpty()) {
Slog.i(TAG, "Package removed or data cleared for user=[" + userId + "], package=["
+ packageName + "]. Cleaning up CDM data...");
-
- for (AssociationInfo association : associationsForPackage) {
- mDisassociationProcessor.disassociate(association.getId());
- }
-
- mCompanionAppBinder.onPackagesChanged(userId, packageName);
+ }
+ for (AssociationInfo association : associationsForPackage) {
+ mDisassociationProcessor.disassociate(association.getId());
}
// Clear observable UUIDs for the package.
@@ -262,16 +273,19 @@ public class CompanionDeviceManagerService extends SystemService {
for (ObservableUuid uuid : uuidsTobeObserved) {
mObservableUuidStore.removeObservableUuid(userId, uuid.getUuid(), packageName);
}
+
+ mCompanionAppBinder.onPackagesChanged(userId);
}
private void onPackageModifiedInternal(@UserIdInt int userId, @NonNull String packageName) {
- final List<AssociationInfo> associations =
+ final List<AssociationInfo> associationsForPackage =
mAssociationStore.getAssociationsByPackage(userId, packageName);
- if (!associations.isEmpty()) {
- mCompanionExemptionProcessor.exemptPackage(userId, packageName, false);
-
- mCompanionAppBinder.onPackagesChanged(userId, packageName);
+ for (AssociationInfo association : associationsForPackage) {
+ updateSpecialAccessPermissionForAssociatedPackage(association.getUserId(),
+ association.getPackageName());
}
+
+ mCompanionAppBinder.onPackagesChanged(userId);
}
private void onPackageAddedInternal(@UserIdInt int userId, @NonNull String packageName) {
@@ -751,6 +765,130 @@ public class CompanionDeviceManagerService extends SystemService {
}
}
+ /**
+ * Update special access for the association's package
+ */
+ public void updateSpecialAccessPermissionForAssociatedPackage(int userId, String packageName) {
+ final PackageInfo packageInfo =
+ getPackageInfo(getContext(), userId, packageName);
+
+ Binder.withCleanCallingIdentity(() -> updateSpecialAccessPermissionAsSystem(packageInfo));
+ }
+
+ private void updateSpecialAccessPermissionAsSystem(PackageInfo packageInfo) {
+ if (packageInfo == null) {
+ return;
+ }
+
+ if (containsEither(packageInfo.requestedPermissions,
+ android.Manifest.permission.RUN_IN_BACKGROUND,
+ android.Manifest.permission.REQUEST_COMPANION_RUN_IN_BACKGROUND)) {
+ mPowerExemptionManager.addToPermanentAllowList(packageInfo.packageName);
+ } else {
+ try {
+ mPowerExemptionManager.removeFromPermanentAllowList(packageInfo.packageName);
+ } catch (UnsupportedOperationException e) {
+ Slog.w(TAG, packageInfo.packageName + " can't be removed from power save"
+ + " whitelist. It might due to the package is whitelisted by the system.");
+ }
+ }
+
+ NetworkPolicyManager networkPolicyManager = NetworkPolicyManager.from(getContext());
+ try {
+ if (containsEither(packageInfo.requestedPermissions,
+ android.Manifest.permission.USE_DATA_IN_BACKGROUND,
+ android.Manifest.permission.REQUEST_COMPANION_USE_DATA_IN_BACKGROUND)) {
+ networkPolicyManager.addUidPolicy(
+ packageInfo.applicationInfo.uid,
+ NetworkPolicyManager.POLICY_ALLOW_METERED_BACKGROUND);
+ } else {
+ networkPolicyManager.removeUidPolicy(
+ packageInfo.applicationInfo.uid,
+ NetworkPolicyManager.POLICY_ALLOW_METERED_BACKGROUND);
+ }
+ } catch (IllegalArgumentException e) {
+ Slog.e(TAG, e.getMessage());
+ }
+
+ exemptFromAutoRevoke(packageInfo.packageName, packageInfo.applicationInfo.uid);
+ }
+
+ private void exemptFromAutoRevoke(String packageName, int uid) {
+ try {
+ mAppOpsManager.setMode(
+ AppOpsManager.OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED,
+ uid,
+ packageName,
+ AppOpsManager.MODE_IGNORED);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Error while granting auto revoke exemption for " + packageName, e);
+ }
+ }
+
+ private void updateAtm(int userId, List<AssociationInfo> associations) {
+ final Set<Integer> companionAppUids = new ArraySet<>();
+ for (AssociationInfo association : associations) {
+ final int uid = mPackageManagerInternal.getPackageUid(association.getPackageName(),
+ 0, userId);
+ if (uid >= 0) {
+ companionAppUids.add(uid);
+ }
+ }
+ if (mAtmInternal != null) {
+ mAtmInternal.setCompanionAppUids(userId, companionAppUids);
+ }
+ if (mAmInternal != null) {
+ // Make a copy of the set and send it to ActivityManager.
+ mAmInternal.setCompanionAppUids(userId, new ArraySet<>(companionAppUids));
+ }
+ }
+
+ private void maybeGrantAutoRevokeExemptions() {
+ Slog.d(TAG, "maybeGrantAutoRevokeExemptions()");
+
+ PackageManager pm = getContext().getPackageManager();
+ for (int userId : LocalServices.getService(UserManagerInternal.class).getUserIds()) {
+ SharedPreferences pref = getContext().getSharedPreferences(
+ new File(Environment.getUserSystemDirectory(userId), PREF_FILE_NAME),
+ Context.MODE_PRIVATE);
+ if (pref.getBoolean(PREF_KEY_AUTO_REVOKE_GRANTS_DONE, false)) {
+ continue;
+ }
+
+ try {
+ final List<AssociationInfo> associations =
+ mAssociationStore.getActiveAssociationsByUser(userId);
+ for (AssociationInfo a : associations) {
+ try {
+ int uid = pm.getPackageUidAsUser(a.getPackageName(), userId);
+ exemptFromAutoRevoke(a.getPackageName(), uid);
+ } catch (PackageManager.NameNotFoundException e) {
+ Slog.w(TAG, "Unknown companion package: " + a.getPackageName(), e);
+ }
+ }
+ } finally {
+ pref.edit().putBoolean(PREF_KEY_AUTO_REVOKE_GRANTS_DONE, true).apply();
+ }
+ }
+ }
+
+ private final AssociationStore.OnChangeListener mAssociationStoreChangeListener =
+ new AssociationStore.OnChangeListener() {
+ @Override
+ public void onAssociationChanged(int changeType, AssociationInfo association) {
+ Slog.d(TAG, "onAssociationChanged changeType=[" + changeType
+ + "], association=[" + association);
+
+ final int userId = association.getUserId();
+ final List<AssociationInfo> updatedAssociations =
+ mAssociationStore.getActiveAssociationsByUser(userId);
+
+ updateAtm(userId, updatedAssociations);
+ updateSpecialAccessPermissionForAssociatedPackage(association.getUserId(),
+ association.getPackageName());
+ }
+ };
+
private final PackageMonitor mPackageMonitor = new PackageMonitor() {
@Override
public void onPackageRemoved(String packageName, int uid) {
@@ -773,6 +911,10 @@ public class CompanionDeviceManagerService extends SystemService {
}
};
+ private static <T> boolean containsEither(T[] array, T a, T b) {
+ return ArrayUtils.contains(array, a) || ArrayUtils.contains(array, b);
+ }
+
private class LocalService implements CompanionDeviceManagerServiceInternal {
@Override
diff --git a/services/companion/java/com/android/server/companion/CompanionExemptionProcessor.java b/services/companion/java/com/android/server/companion/CompanionExemptionProcessor.java
deleted file mode 100644
index 4969ffbfa08a..000000000000
--- a/services/companion/java/com/android/server/companion/CompanionExemptionProcessor.java
+++ /dev/null
@@ -1,219 +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.companion;
-
-import static android.app.AppOpsManager.MODE_ALLOWED;
-import static android.app.AppOpsManager.MODE_IGNORED;
-
-import static com.android.server.companion.utils.PackageUtils.getPackageInfo;
-
-import android.annotation.SuppressLint;
-import android.app.ActivityManagerInternal;
-import android.app.AppOpsManager;
-import android.companion.AssociationInfo;
-import android.content.Context;
-import android.content.SharedPreferences;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManagerInternal;
-import android.net.NetworkPolicyManager;
-import android.os.Binder;
-import android.os.Environment;
-import android.os.PowerExemptionManager;
-import android.util.ArraySet;
-import android.util.Slog;
-
-import com.android.internal.util.ArrayUtils;
-import com.android.server.LocalServices;
-import com.android.server.companion.association.AssociationStore;
-import com.android.server.pm.UserManagerInternal;
-import com.android.server.wm.ActivityTaskManagerInternal;
-
-import java.io.File;
-import java.util.List;
-import java.util.Set;
-
-@SuppressLint("LongLogTag")
-public class CompanionExemptionProcessor {
-
- private static final String TAG = "CDM_CompanionExemptionProcessor";
-
- private static final String PREF_FILE_NAME = "companion_device_preferences.xml";
- private static final String PREF_KEY_AUTO_REVOKE_GRANTS_DONE = "auto_revoke_grants_done";
-
- private final Context mContext;
- private final PowerExemptionManager mPowerExemptionManager;
- private final AppOpsManager mAppOpsManager;
- private final PackageManagerInternal mPackageManager;
- private final ActivityTaskManagerInternal mAtmInternal;
- private final ActivityManagerInternal mAmInternal;
- private final AssociationStore mAssociationStore;
-
- public CompanionExemptionProcessor(Context context, PowerExemptionManager powerExemptionManager,
- AppOpsManager appOpsManager, PackageManagerInternal packageManager,
- ActivityTaskManagerInternal atmInternal, ActivityManagerInternal amInternal,
- AssociationStore associationStore) {
- mContext = context;
- mPowerExemptionManager = powerExemptionManager;
- mAppOpsManager = appOpsManager;
- mPackageManager = packageManager;
- mAtmInternal = atmInternal;
- mAmInternal = amInternal;
- mAssociationStore = associationStore;
-
- mAssociationStore.registerLocalListener(new AssociationStore.OnChangeListener() {
- @Override
- public void onAssociationChanged(int changeType, AssociationInfo association) {
- final int userId = association.getUserId();
- final List<AssociationInfo> updatedAssociations =
- mAssociationStore.getActiveAssociationsByUser(userId);
-
- updateAtm(userId, updatedAssociations);
- }
- });
- }
-
- /**
- * Update ActivityManager and ActivityTaskManager exemptions
- */
- public void updateAtm(int userId, List<AssociationInfo> associations) {
- final Set<Integer> companionAppUids = new ArraySet<>();
- for (AssociationInfo association : associations) {
- int uid = mPackageManager.getPackageUid(association.getPackageName(), 0, userId);
- if (uid >= 0) {
- companionAppUids.add(uid);
- }
- }
- if (mAtmInternal != null) {
- mAtmInternal.setCompanionAppUids(userId, companionAppUids);
- }
- if (mAmInternal != null) {
- // Make a copy of the set and send it to ActivityManager.
- mAmInternal.setCompanionAppUids(userId, new ArraySet<>(companionAppUids));
- }
- }
-
- /**
- * Update special access for the association's package
- */
- public void exemptPackage(int userId, String packageName, boolean hasPresentDevices) {
- Slog.i(TAG, "Exempting package [" + packageName + "]...");
-
- final PackageInfo packageInfo = getPackageInfo(mContext, userId, packageName);
-
- Binder.withCleanCallingIdentity(
- () -> exemptPackageAsSystem(userId, packageInfo, hasPresentDevices));
- }
-
- @SuppressLint("MissingPermission")
- private void exemptPackageAsSystem(int userId, PackageInfo packageInfo,
- boolean hasPresentDevices) {
- if (packageInfo == null) {
- return;
- }
-
- // If the app has run-in-bg permission and present devices, add it to power saver allowlist.
- if (containsEither(packageInfo.requestedPermissions,
- android.Manifest.permission.RUN_IN_BACKGROUND,
- android.Manifest.permission.REQUEST_COMPANION_RUN_IN_BACKGROUND)
- && hasPresentDevices) {
- mPowerExemptionManager.addToPermanentAllowList(packageInfo.packageName);
- } else {
- try {
- mPowerExemptionManager.removeFromPermanentAllowList(packageInfo.packageName);
- } catch (UnsupportedOperationException e) {
- Slog.w(TAG, packageInfo.packageName + " can't be removed from power save"
- + " allowlist. It might be due to the package being allowlisted by the"
- + " system.");
- }
- }
-
- // If the app has run-in-bg permission and present device, allow metered network use.
- NetworkPolicyManager networkPolicyManager = NetworkPolicyManager.from(mContext);
- try {
- if (containsEither(packageInfo.requestedPermissions,
- android.Manifest.permission.USE_DATA_IN_BACKGROUND,
- android.Manifest.permission.REQUEST_COMPANION_USE_DATA_IN_BACKGROUND)
- && hasPresentDevices) {
- networkPolicyManager.addUidPolicy(
- packageInfo.applicationInfo.uid,
- NetworkPolicyManager.POLICY_ALLOW_METERED_BACKGROUND);
- } else {
- networkPolicyManager.removeUidPolicy(
- packageInfo.applicationInfo.uid,
- NetworkPolicyManager.POLICY_ALLOW_METERED_BACKGROUND);
- }
- } catch (IllegalArgumentException e) {
- Slog.e(TAG, e.getMessage());
- }
-
- updateAutoRevokeExemption(packageInfo.packageName, packageInfo.applicationInfo.uid,
- !mAssociationStore.getActiveAssociationsByPackage(userId,
- packageInfo.packageName).isEmpty());
- }
-
- /**
- * Update auto revoke exemptions.
- * If the app has any association, exempt it from permission auto revoke.
- */
- public void updateAutoRevokeExemptions() {
- Slog.d(TAG, "maybeGrantAutoRevokeExemptions()");
-
- PackageManager pm = mContext.getPackageManager();
- for (int userId : LocalServices.getService(UserManagerInternal.class).getUserIds()) {
- SharedPreferences pref = mContext.getSharedPreferences(
- new File(Environment.getUserSystemDirectory(userId), PREF_FILE_NAME),
- Context.MODE_PRIVATE);
- if (pref.getBoolean(PREF_KEY_AUTO_REVOKE_GRANTS_DONE, false)) {
- continue;
- }
-
- try {
- final List<AssociationInfo> associations =
- mAssociationStore.getActiveAssociationsByUser(userId);
- for (AssociationInfo a : associations) {
- try {
- int uid = pm.getPackageUidAsUser(a.getPackageName(), userId);
- updateAutoRevokeExemption(a.getPackageName(), uid, true);
- } catch (PackageManager.NameNotFoundException e) {
- Slog.w(TAG, "Unknown companion package: " + a.getPackageName(), e);
- }
- }
- } finally {
- pref.edit().putBoolean(PREF_KEY_AUTO_REVOKE_GRANTS_DONE, true).apply();
- }
- }
- }
-
- @SuppressLint("MissingPermission")
- private void updateAutoRevokeExemption(String packageName, int uid, boolean hasAssociations) {
- try {
- mAppOpsManager.setMode(
- AppOpsManager.OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED,
- uid,
- packageName,
- hasAssociations ? MODE_IGNORED : MODE_ALLOWED);
- } catch (Exception e) {
- Slog.e(TAG, "Error while granting auto revoke exemption for " + packageName, e);
- }
- }
-
- private <T> boolean containsEither(T[] array, T a, T b) {
- return ArrayUtils.contains(array, a) || ArrayUtils.contains(array, b);
- }
-
-}
diff --git a/services/companion/java/com/android/server/companion/devicepresence/CompanionAppBinder.java b/services/companion/java/com/android/server/companion/devicepresence/CompanionAppBinder.java
index 1eb6d13feeac..60f46887fa5c 100644
--- a/services/companion/java/com/android/server/companion/devicepresence/CompanionAppBinder.java
+++ b/services/companion/java/com/android/server/companion/devicepresence/CompanionAppBinder.java
@@ -95,10 +95,7 @@ public class CompanionAppBinder {
/**
* On package changed.
*/
- public void onPackagesChanged(@UserIdInt int userId, String packageName) {
- // TODO: We shouldn't need to clean up the whole user registry. We only need to remove the
- // package. I will do it in a separate change since it's not appropriate to use
- // PerUser anymore.
+ public void onPackagesChanged(@UserIdInt int userId) {
mCompanionServicesRegister.invalidate(userId);
}
diff --git a/services/companion/java/com/android/server/companion/devicepresence/DevicePresenceProcessor.java b/services/companion/java/com/android/server/companion/devicepresence/DevicePresenceProcessor.java
index 7b4dd7df8be3..a374d279af0b 100644
--- a/services/companion/java/com/android/server/companion/devicepresence/DevicePresenceProcessor.java
+++ b/services/companion/java/com/android/server/companion/devicepresence/DevicePresenceProcessor.java
@@ -57,7 +57,6 @@ import android.util.SparseBooleanArray;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.CollectionUtils;
-import com.android.server.companion.CompanionExemptionProcessor;
import com.android.server.companion.association.AssociationStore;
import java.io.PrintWriter;
@@ -102,8 +101,6 @@ public class DevicePresenceProcessor implements AssociationStore.OnChangeListene
private final PowerManagerInternal mPowerManagerInternal;
@NonNull
private final UserManager mUserManager;
- @NonNull
- private final CompanionExemptionProcessor mCompanionExemptionProcessor;
// NOTE: Same association may appear in more than one of the following sets at the same time.
// (E.g. self-managed devices that have MAC addresses, could be reported as present by their
@@ -114,7 +111,7 @@ public class DevicePresenceProcessor implements AssociationStore.OnChangeListene
@NonNull
private final Set<Integer> mNearbyBleDevices = new HashSet<>();
@NonNull
- private final Set<Integer> mConnectedSelfManagedDevices = new HashSet<>();
+ private final Set<Integer> mReportedSelfManagedDevices = new HashSet<>();
@NonNull
private final Set<ParcelUuid> mConnectedUuidDevices = new HashSet<>();
@NonNull
@@ -149,8 +146,7 @@ public class DevicePresenceProcessor implements AssociationStore.OnChangeListene
@NonNull UserManager userManager,
@NonNull AssociationStore associationStore,
@NonNull ObservableUuidStore observableUuidStore,
- @NonNull PowerManagerInternal powerManagerInternal,
- @NonNull CompanionExemptionProcessor companionExemptionProcessor) {
+ @NonNull PowerManagerInternal powerManagerInternal) {
mContext = context;
mCompanionAppBinder = companionAppBinder;
mAssociationStore = associationStore;
@@ -160,7 +156,6 @@ public class DevicePresenceProcessor implements AssociationStore.OnChangeListene
mObservableUuidStore, this);
mBleDeviceProcessor = new BleDeviceProcessor(associationStore, this);
mPowerManagerInternal = powerManagerInternal;
- mCompanionExemptionProcessor = companionExemptionProcessor;
}
/** Initialize {@link DevicePresenceProcessor} */
@@ -409,7 +404,7 @@ public class DevicePresenceProcessor implements AssociationStore.OnChangeListene
* nearby (for "self-managed" associations).
*/
public boolean isDevicePresent(int associationId) {
- return mConnectedSelfManagedDevices.contains(associationId)
+ return mReportedSelfManagedDevices.contains(associationId)
|| mConnectedBtDevices.contains(associationId)
|| mNearbyBleDevices.contains(associationId)
|| mSimulated.contains(associationId);
@@ -456,7 +451,7 @@ public class DevicePresenceProcessor implements AssociationStore.OnChangeListene
* notifyDeviceAppeared()}
*/
public void onSelfManagedDeviceConnected(int associationId) {
- onDevicePresenceEvent(mConnectedSelfManagedDevices,
+ onDevicePresenceEvent(mReportedSelfManagedDevices,
associationId, EVENT_SELF_MANAGED_APPEARED);
}
@@ -472,7 +467,7 @@ public class DevicePresenceProcessor implements AssociationStore.OnChangeListene
* notifyDeviceDisappeared()}
*/
public void onSelfManagedDeviceDisconnected(int associationId) {
- onDevicePresenceEvent(mConnectedSelfManagedDevices,
+ onDevicePresenceEvent(mReportedSelfManagedDevices,
associationId, EVENT_SELF_MANAGED_DISAPPEARED);
}
@@ -480,7 +475,7 @@ public class DevicePresenceProcessor implements AssociationStore.OnChangeListene
* Marks a "self-managed" device as disconnected when binderDied.
*/
public void onSelfManagedDeviceReporterBinderDied(int associationId) {
- onDevicePresenceEvent(mConnectedSelfManagedDevices,
+ onDevicePresenceEvent(mReportedSelfManagedDevices,
associationId, EVENT_SELF_MANAGED_DISAPPEARED);
}
@@ -688,7 +683,6 @@ public class DevicePresenceProcessor implements AssociationStore.OnChangeListene
if (association.shouldBindWhenPresent()) {
bindApplicationIfNeeded(userId, packageName, association.isSelfManaged());
- mCompanionExemptionProcessor.exemptPackage(userId, packageName, true);
} else {
return;
}
@@ -721,7 +715,6 @@ public class DevicePresenceProcessor implements AssociationStore.OnChangeListene
// Check if there are other devices associated to the app that are present.
if (!shouldBindPackage(userId, packageName)) {
mCompanionAppBinder.unbindCompanionApp(userId, packageName);
- mCompanionExemptionProcessor.exemptPackage(userId, packageName, false);
}
break;
default:
@@ -947,7 +940,7 @@ public class DevicePresenceProcessor implements AssociationStore.OnChangeListene
mConnectedBtDevices.remove(id);
mNearbyBleDevices.remove(id);
- mConnectedSelfManagedDevices.remove(id);
+ mReportedSelfManagedDevices.remove(id);
mSimulated.remove(id);
synchronized (mBtDisconnectedDevices) {
mBtDisconnectedDevices.remove(id);
@@ -1107,7 +1100,7 @@ public class DevicePresenceProcessor implements AssociationStore.OnChangeListene
out.append("Companion Device Present: ");
if (mConnectedBtDevices.isEmpty()
&& mNearbyBleDevices.isEmpty()
- && mConnectedSelfManagedDevices.isEmpty()) {
+ && mReportedSelfManagedDevices.isEmpty()) {
out.append("<empty>\n");
return;
} else {
@@ -1137,11 +1130,11 @@ public class DevicePresenceProcessor implements AssociationStore.OnChangeListene
}
out.append(" Self-Reported Devices: ");
- if (mConnectedSelfManagedDevices.isEmpty()) {
+ if (mReportedSelfManagedDevices.isEmpty()) {
out.append("<empty>\n");
} else {
out.append("\n");
- for (int associationId : mConnectedSelfManagedDevices) {
+ for (int associationId : mReportedSelfManagedDevices) {
AssociationInfo a = mAssociationStore.getAssociationById(associationId);
out.append(" ").append(a.toShortString()).append('\n');
}
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 348d83fea81d..6cfd44bb2d1a 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -126,6 +126,7 @@ java_library_static {
"platform_service_defaults",
"android.hardware.power-java_shared",
"latest_android_hardware_broadcastradio_java_static",
+ "services_crashrecovery_stubs_conditionally",
],
srcs: [
":android.hardware.tv.hdmi.connection-V1-java-source",
diff --git a/services/core/java/com/android/server/VcnManagementService.java b/services/core/java/com/android/server/VcnManagementService.java
index dc7749cb02ef..06e6c8b1ec53 100644
--- a/services/core/java/com/android/server/VcnManagementService.java
+++ b/services/core/java/com/android/server/VcnManagementService.java
@@ -525,6 +525,9 @@ public class VcnManagementService extends IVcnManagementService.Stub {
@Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
+ if (action == null) {
+ return;
+ }
switch (action) {
case Intent.ACTION_PACKAGE_ADDED: // Fallthrough
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 2a9cb435e01d..1c3569dd52d0 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -60,7 +60,6 @@ import static android.app.ProcessMemoryState.HOSTING_COMPONENT_TYPE_BACKUP;
import static android.app.ProcessMemoryState.HOSTING_COMPONENT_TYPE_INSTRUMENTATION;
import static android.app.ProcessMemoryState.HOSTING_COMPONENT_TYPE_PERSISTENT;
import static android.app.ProcessMemoryState.HOSTING_COMPONENT_TYPE_SYSTEM;
-import static android.content.Intent.isPreventIntentRedirectEnabled;
import static android.content.pm.ApplicationInfo.HIDDEN_API_ENFORCEMENT_DEFAULT;
import static android.content.pm.PackageManager.GET_SHARED_LIBRARY_FILES;
import static android.content.pm.PackageManager.MATCH_ALL;
@@ -131,6 +130,7 @@ import static android.os.Process.setThreadScheduler;
import static android.provider.Settings.Global.ALWAYS_FINISH_ACTIVITIES;
import static android.provider.Settings.Global.DEBUG_APP;
import static android.provider.Settings.Global.WAIT_FOR_DEBUGGER;
+import static android.security.Flags.preventIntentRedirect;
import static android.util.FeatureFlagUtils.SETTINGS_ENABLE_MONITOR_PHANTOM_PROCS;
import static android.view.Display.INVALID_DISPLAY;
@@ -14301,6 +14301,10 @@ public class ActivityManagerService extends IActivityManager.Stub
mBroadcastController.unregisterReceiver(receiver);
}
+ public List<IntentFilter> getRegisteredIntentFilters(IIntentReceiver receiver) {
+ return mBroadcastController.getRegisteredIntentFilters(receiver);
+ }
+
@GuardedBy("this")
final int broadcastIntentLocked(ProcessRecord callerApp,
String callerPackage, String callerFeatureId, Intent intent, String resolvedType,
@@ -19281,7 +19285,7 @@ public class ActivityManagerService extends IActivityManager.Stub
* @hide
*/
public void addCreatorToken(@Nullable Intent intent, String creatorPackage) {
- if (!isPreventIntentRedirectEnabled()) return;
+ if (!preventIntentRedirect()) return;
if (intent == null || intent.getExtraIntentKeys() == null) return;
for (String key : intent.getExtraIntentKeys()) {
diff --git a/services/core/java/com/android/server/am/BroadcastController.java b/services/core/java/com/android/server/am/BroadcastController.java
index 71ddf05eb692..b0f880710eb6 100644
--- a/services/core/java/com/android/server/am/BroadcastController.java
+++ b/services/core/java/com/android/server/am/BroadcastController.java
@@ -679,6 +679,21 @@ class BroadcastController {
}
}
+ List<IntentFilter> getRegisteredIntentFilters(IIntentReceiver receiver) {
+ synchronized (mService) {
+ final ReceiverList rl = mRegisteredReceivers.get(receiver.asBinder());
+ if (rl == null) {
+ return null;
+ }
+ final ArrayList<IntentFilter> filters = new ArrayList<>();
+ final int count = rl.size();
+ for (int i = 0; i < count; ++i) {
+ filters.add(rl.get(i));
+ }
+ return filters;
+ }
+ }
+
int broadcastIntentWithFeature(IApplicationThread caller, String callingFeatureId,
Intent intent, String resolvedType, IIntentReceiver resultTo,
int resultCode, String resultData, Bundle resultExtras,
diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
index 7cfe8292d52f..bcde1ffe87d6 100644
--- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
+++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
@@ -209,6 +209,7 @@ public class SettingsToPropertiesMapper {
"pixel_perf",
"pixel_sensors",
"pixel_system_sw_video",
+ "pixel_video_sw",
"pixel_watch",
"platform_compat",
"platform_security",
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index 906e584af1ad..a3b20b93ef02 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -693,6 +693,8 @@ public class AudioDeviceBroker {
elapsed = System.currentTimeMillis() - start;
if (elapsed >= SET_COMMUNICATION_DEVICE_TIMEOUT_MS) {
Log.e(TAG, "Timeout waiting for communication device update.");
+ // reset counter to avoid sticky out of sync condition
+ mCommunicationDeviceUpdateCount = 0;
break;
}
}
@@ -1342,8 +1344,8 @@ public class AudioDeviceBroker {
}
/*package*/ void postSetModeOwner(int mode, int pid, int uid, boolean signal) {
- sendILMsgNoDelay(MSG_IL_SET_MODE_OWNER, SENDMSG_REPLACE,
- signal ? 1 : 0, new AudioModeInfo(mode, pid, uid));
+ sendLMsgNoDelay(signal ? MSG_L_SET_MODE_OWNER_SIGNAL : MSG_L_SET_MODE_OWNER,
+ SENDMSG_REPLACE, new AudioModeInfo(mode, pid, uid));
}
/*package*/ void postBluetoothDeviceConfigChange(@NonNull BtDeviceInfo info) {
@@ -2026,7 +2028,8 @@ public class AudioDeviceBroker {
mBtHelper.setAvrcpAbsoluteVolumeIndex(msg.arg1);
}
break;
- case MSG_IL_SET_MODE_OWNER:
+ case MSG_L_SET_MODE_OWNER:
+ case MSG_L_SET_MODE_OWNER_SIGNAL:
synchronized (mSetModeLock) {
synchronized (mDeviceStateLock) {
mAudioModeOwner = (AudioModeInfo) msg.obj;
@@ -2037,7 +2040,7 @@ public class AudioDeviceBroker {
}
}
}
- if (msg.arg1 == 1 /*signal*/) {
+ if (msg.what == MSG_L_SET_MODE_OWNER_SIGNAL) {
mAudioService.decrementAudioModeResetCount();
}
break;
@@ -2199,7 +2202,8 @@ public class AudioDeviceBroker {
private static final int MSG_REPORT_NEW_ROUTES = 13;
private static final int MSG_II_SET_HEARING_AID_VOLUME = 14;
private static final int MSG_I_SET_AVRCP_ABSOLUTE_VOLUME = 15;
- private static final int MSG_IL_SET_MODE_OWNER = 16;
+ private static final int MSG_L_SET_MODE_OWNER = 16;
+ private static final int MSG_L_SET_MODE_OWNER_SIGNAL = 17;
private static final int MSG_I_BT_SERVICE_DISCONNECTED_PROFILE = 22;
private static final int MSG_IL_BT_SERVICE_CONNECTED_PROFILE = 23;
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 3dbe48a2e540..985155d0d891 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -457,7 +457,7 @@ public class AudioService extends IAudioService.Stub
private static final int MSG_UPDATE_AUDIO_MODE = 36;
private static final int MSG_RECORDING_CONFIG_CHANGE = 37;
private static final int MSG_BT_DEV_CHANGED = 38;
-
+ private static final int MSG_UPDATE_AUDIO_MODE_SIGNAL = 39;
private static final int MSG_DISPATCH_AUDIO_MODE = 40;
private static final int MSG_ROUTING_UPDATED = 41;
private static final int MSG_INIT_HEADTRACKING_SENSORS = 42;
@@ -4804,16 +4804,14 @@ public class AudioService extends IAudioService.Stub
}
static class UpdateAudioModeInfo {
- UpdateAudioModeInfo(int mode, int pid, String packageName, boolean signal) {
+ UpdateAudioModeInfo(int mode, int pid, String packageName) {
mMode = mode;
mPid = pid;
mPackageName = packageName;
- mSignal = signal;
}
private final int mMode;
private final int mPid;
private final String mPackageName;
- private final boolean mSignal;
int getMode() {
return mMode;
@@ -4824,9 +4822,6 @@ public class AudioService extends IAudioService.Stub
String getPackageName() {
return mPackageName;
}
- boolean getSignal() {
- return mSignal;
- }
}
void postUpdateAudioMode(int msgPolicy, int mode, int pid, String packageName,
@@ -4835,8 +4830,8 @@ public class AudioService extends IAudioService.Stub
if (signal) {
mAudioModeResetCount++;
}
- sendMsg(mAudioHandler, MSG_UPDATE_AUDIO_MODE, msgPolicy, 0, 0,
- new UpdateAudioModeInfo(mode, pid, packageName, signal), delay);
+ sendMsg(mAudioHandler, signal ? MSG_UPDATE_AUDIO_MODE_SIGNAL : MSG_UPDATE_AUDIO_MODE,
+ msgPolicy, 0, 0, new UpdateAudioModeInfo(mode, pid, packageName), delay);
}
}
@@ -6654,6 +6649,9 @@ public class AudioService extends IAudioService.Stub
// connections not started by the application changing the mode when pid changes
mDeviceBroker.postSetModeOwner(mode, pid, uid, signal);
} else {
+ // reset here to avoid sticky out of sync condition (would have been reset
+ // by AudioDeviceBroker processing MSG_L_SET_MODE_OWNER_SIGNAL message)
+ resetAudioModeResetCount();
Log.w(TAG, "onUpdateAudioMode: failed to set audio mode to: " + mode);
}
}
@@ -10420,10 +10418,11 @@ public class AudioService extends IAudioService.Stub
break;
case MSG_UPDATE_AUDIO_MODE:
+ case MSG_UPDATE_AUDIO_MODE_SIGNAL:
synchronized (mDeviceBroker.mSetModeLock) {
UpdateAudioModeInfo info = (UpdateAudioModeInfo) msg.obj;
onUpdateAudioMode(info.getMode(), info.getPid(), info.getPackageName(),
- false /*force*/, info.getSignal());
+ false /*force*/, msg.what == MSG_UPDATE_AUDIO_MODE_SIGNAL);
}
break;
@@ -11164,6 +11163,8 @@ public class AudioService extends IAudioService.Stub
elapsed = java.lang.System.currentTimeMillis() - start;
if (elapsed >= AUDIO_MODE_RESET_TIMEOUT_MS) {
Log.e(TAG, "Timeout waiting for audio mode reset");
+ // reset count to avoid sticky out of sync state.
+ resetAudioModeResetCount();
break;
}
}
@@ -11175,7 +11176,7 @@ public class AudioService extends IAudioService.Stub
return mMediaFocusControl.abandonAudioFocus(fd, clientId, aa, callingPackageName);
}
- /** synchronization between setMode(NORMAL) and abandonAudioFocus() frmo Telecom */
+ /** synchronization between setMode(NORMAL) and abandonAudioFocus() from Telecom */
private static final long AUDIO_MODE_RESET_TIMEOUT_MS = 3000;
private final Object mAudioModeResetLock = new Object();
@@ -11194,6 +11195,13 @@ public class AudioService extends IAudioService.Stub
}
}
+ private void resetAudioModeResetCount() {
+ synchronized (mAudioModeResetLock) {
+ mAudioModeResetCount = 0;
+ mAudioModeResetLock.notify();
+ }
+ }
+
/** see {@link AudioManager#abandonAudioFocusForTest(AudioFocusRequest, String)} */
public int abandonAudioFocusForTest(IAudioFocusDispatcher fd, String clientId,
AudioAttributes aa, String callingPackageName) {
diff --git a/services/core/java/com/android/server/audio/MediaFocusControl.java b/services/core/java/com/android/server/audio/MediaFocusControl.java
index 1604e94e5a6d..1da62d74f0df 100644
--- a/services/core/java/com/android/server/audio/MediaFocusControl.java
+++ b/services/core/java/com/android/server/audio/MediaFocusControl.java
@@ -1068,7 +1068,6 @@ public class MediaFocusControl implements PlayerFocusEnforcer {
switch (attr.getUsage()) {
case AudioAttributes.USAGE_MEDIA:
case AudioAttributes.USAGE_GAME:
- case AudioAttributes.USAGE_SPEAKER_CLEANUP:
return 1000;
case AudioAttributes.USAGE_ALARM:
case AudioAttributes.USAGE_NOTIFICATION_RINGTONE:
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 3603cdbd775f..f5a75c7d1c38 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -25,7 +25,7 @@ import static android.Manifest.permission.MANAGE_DISPLAYS;
import static android.Manifest.permission.RESTRICT_DISPLAY_MODES;
import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_CACHED;
import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_GONE;
-import static android.hardware.display.DisplayManager.EventsMask;
+import static android.hardware.display.DisplayManager.EventFlag;
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_ALWAYS_UNLOCKED;
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR;
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD;
@@ -1390,16 +1390,16 @@ public final class DisplayManagerService extends SystemService {
}
private void registerCallbackInternal(IDisplayManagerCallback callback, int callingPid,
- int callingUid, @EventsMask long eventsMask) {
+ int callingUid, @EventFlag long eventFlagsMask) {
synchronized (mSyncRoot) {
CallbackRecord record = mCallbacks.get(callingPid);
if (record != null) {
- record.updateEventsMask(eventsMask);
+ record.updateEventFlagsMask(eventFlagsMask);
return;
}
- record = new CallbackRecord(callingPid, callingUid, callback, eventsMask);
+ record = new CallbackRecord(callingPid, callingUid, callback, eventFlagsMask);
try {
IBinder binder = callback.asBinder();
binder.linkToDeath(record, 0);
@@ -1889,6 +1889,7 @@ public final class DisplayManagerService extends SystemService {
final String displayUniqueId = VirtualDisplayAdapter.generateDisplayUniqueId(
packageName, callingUid, virtualDisplayConfig);
+ boolean shouldClearDisplayWindowSettings = false;
if (virtualDisplayConfig.isHomeSupported()) {
if ((flags & VIRTUAL_DISPLAY_FLAG_TRUSTED) == 0) {
Slog.w(TAG, "Display created with home support but lacks "
@@ -1900,6 +1901,18 @@ public final class DisplayManagerService extends SystemService {
} else {
mWindowManagerInternal.setHomeSupportedOnDisplay(displayUniqueId,
Display.TYPE_VIRTUAL, true);
+ shouldClearDisplayWindowSettings = true;
+ }
+ }
+
+ if (virtualDisplayConfig.isIgnoreActivitySizeRestrictions()) {
+ if ((flags & VIRTUAL_DISPLAY_FLAG_TRUSTED) == 0) {
+ Slog.w(TAG, "Display created to ignore activity size restrictions, "
+ + "but lacks VIRTUAL_DISPLAY_FLAG_TRUSTED, ignoring the request.");
+ } else {
+ mWindowManagerInternal.setIgnoreActivitySizeRestrictionsOnDisplay(
+ displayUniqueId, Display.TYPE_VIRTUAL, true);
+ shouldClearDisplayWindowSettings = true;
}
}
@@ -1922,8 +1935,7 @@ public final class DisplayManagerService extends SystemService {
}
}
- if (displayId == Display.INVALID_DISPLAY && virtualDisplayConfig.isHomeSupported()
- && (flags & VIRTUAL_DISPLAY_FLAG_TRUSTED) != 0) {
+ if (displayId == Display.INVALID_DISPLAY && shouldClearDisplayWindowSettings) {
// Failed to create the virtual display, so we should clean up the WM settings
// because it won't receive the onDisplayRemoved callback.
mWindowManagerInternal.clearDisplaySettings(displayUniqueId, Display.TYPE_VIRTUAL);
@@ -3997,7 +4009,7 @@ public final class DisplayManagerService extends SystemService {
public final int mPid;
public final int mUid;
private final IDisplayManagerCallback mCallback;
- private @EventsMask AtomicLong mEventsMask;
+ private @DisplayManager.EventFlag AtomicLong mEventFlagsMask;
private final String mPackageName;
public boolean mWifiDisplayScanRequested;
@@ -4018,11 +4030,11 @@ public final class DisplayManagerService extends SystemService {
private boolean mFrozen;
CallbackRecord(int pid, int uid, @NonNull IDisplayManagerCallback callback,
- @EventsMask long eventsMask) {
+ @EventFlag long eventFlagsMask) {
mPid = pid;
mUid = uid;
mCallback = callback;
- mEventsMask = new AtomicLong(eventsMask);
+ mEventFlagsMask = new AtomicLong(eventFlagsMask);
mCached = false;
mFrozen = false;
@@ -4044,8 +4056,8 @@ public final class DisplayManagerService extends SystemService {
mPackageName = packageNames == null ? null : packageNames[0];
}
- public void updateEventsMask(@EventsMask long eventsMask) {
- mEventsMask.set(eventsMask);
+ public void updateEventFlagsMask(@EventFlag long eventFlag) {
+ mEventFlagsMask.set(eventFlag);
}
/**
@@ -4109,12 +4121,13 @@ public final class DisplayManagerService extends SystemService {
if (!shouldSendEvent(event)) {
if (extraLogging(mPackageName)) {
Slog.i(TAG,
- "Not sending displayEvent: " + event + " due to mask:" + mEventsMask);
+ "Not sending displayEvent: " + event + " due to flag:"
+ + mEventFlagsMask);
}
if (Trace.isTagEnabled(Trace.TRACE_TAG_POWER)) {
Trace.instant(Trace.TRACE_TAG_POWER,
- "notifyDisplayEventAsync#notSendingEvent=" + event + ",mEventsMask="
- + mEventsMask);
+ "notifyDisplayEventAsync#notSendingEvent=" + event + ",mEventsFlag="
+ + mEventFlagsMask);
}
// The client is not interested in this event, so do nothing.
return true;
@@ -4160,22 +4173,22 @@ public final class DisplayManagerService extends SystemService {
* Return true if the client is interested in this event.
*/
private boolean shouldSendEvent(@DisplayEvent int event) {
- final long mask = mEventsMask.get();
+ final long flag = mEventFlagsMask.get();
switch (event) {
case DisplayManagerGlobal.EVENT_DISPLAY_ADDED:
- return (mask & DisplayManager.EVENT_FLAG_DISPLAY_ADDED) != 0;
+ return (flag & DisplayManager.EVENT_FLAG_DISPLAY_ADDED) != 0;
case DisplayManagerGlobal.EVENT_DISPLAY_CHANGED:
- return (mask & DisplayManager.EVENT_FLAG_DISPLAY_CHANGED) != 0;
+ return (flag & DisplayManager.EVENT_FLAG_DISPLAY_CHANGED) != 0;
case DisplayManagerGlobal.EVENT_DISPLAY_BRIGHTNESS_CHANGED:
- return (mask & DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS) != 0;
+ return (flag & DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS) != 0;
case DisplayManagerGlobal.EVENT_DISPLAY_REMOVED:
- return (mask & DisplayManager.EVENT_FLAG_DISPLAY_REMOVED) != 0;
+ return (flag & DisplayManager.EVENT_FLAG_DISPLAY_REMOVED) != 0;
case DisplayManagerGlobal.EVENT_DISPLAY_HDR_SDR_RATIO_CHANGED:
- return (mask & DisplayManager.EVENT_FLAG_HDR_SDR_RATIO_CHANGED) != 0;
+ return (flag & DisplayManager.EVENT_FLAG_HDR_SDR_RATIO_CHANGED) != 0;
case DisplayManagerGlobal.EVENT_DISPLAY_CONNECTED:
// fallthrough
case DisplayManagerGlobal.EVENT_DISPLAY_DISCONNECTED:
- return (mask & DisplayManager.EVENT_FLAG_DISPLAY_CONNECTION_CHANGED) != 0;
+ return (flag & DisplayManager.EVENT_FLAG_DISPLAY_CONNECTION_CHANGED) != 0;
default:
// This should never happen.
Slog.e(TAG, "Unknown display event " + event);
@@ -4369,7 +4382,7 @@ public final class DisplayManagerService extends SystemService {
@Override // Binder call
@SuppressLint("AndroidFrameworkRequiresPermission") // Permission only required sometimes
public void registerCallbackWithEventMask(IDisplayManagerCallback callback,
- @EventsMask long eventsMask) {
+ @EventFlag long eventFlagsMask) {
if (callback == null) {
throw new IllegalArgumentException("listener must not be null");
}
@@ -4378,7 +4391,7 @@ public final class DisplayManagerService extends SystemService {
final int callingUid = Binder.getCallingUid();
if (mFlags.isConnectedDisplayManagementEnabled()) {
- if ((eventsMask & DisplayManager.EVENT_FLAG_DISPLAY_CONNECTION_CHANGED) != 0) {
+ if ((eventFlagsMask & DisplayManager.EVENT_FLAG_DISPLAY_CONNECTION_CHANGED) != 0) {
mContext.enforceCallingOrSelfPermission(MANAGE_DISPLAYS,
"Permission required to get signals about connection events.");
}
@@ -4386,7 +4399,7 @@ public final class DisplayManagerService extends SystemService {
final long token = Binder.clearCallingIdentity();
try {
- registerCallbackInternal(callback, callingPid, callingUid, eventsMask);
+ registerCallbackInternal(callback, callingPid, callingUid, eventFlagsMask);
} finally {
Binder.restoreCallingIdentity(token);
}
diff --git a/services/core/java/com/android/server/input/InputGestureManager.java b/services/core/java/com/android/server/input/InputGestureManager.java
index ae31b33f8c20..f4bd402e63a2 100644
--- a/services/core/java/com/android/server/input/InputGestureManager.java
+++ b/services/core/java/com/android/server/input/InputGestureManager.java
@@ -17,10 +17,13 @@
package com.android.server.input;
import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
import android.hardware.input.InputGestureData;
import android.hardware.input.InputManager;
import android.util.IndentingPrintWriter;
import android.util.SparseArray;
+import android.view.KeyEvent;
import com.android.internal.annotations.GuardedBy;
@@ -40,6 +43,10 @@ import java.util.Objects;
final class InputGestureManager {
private static final String TAG = "InputGestureManager";
+ private static final int KEY_GESTURE_META_MASK =
+ KeyEvent.META_CTRL_ON | KeyEvent.META_ALT_ON | KeyEvent.META_SHIFT_ON
+ | KeyEvent.META_META_ON;
+
@GuardedBy("mCustomInputGestures")
private final SparseArray<Map<InputGestureData.Trigger, InputGestureData>>
mCustomInputGestures = new SparseArray<>();
@@ -96,6 +103,23 @@ final class InputGestureManager {
}
}
+ @Nullable
+ public InputGestureData getCustomGestureForKeyEvent(@UserIdInt int userId, KeyEvent event) {
+ final int keyCode = event.getKeyCode();
+ if (keyCode == KeyEvent.KEYCODE_UNKNOWN) {
+ return null;
+ }
+ synchronized (mCustomInputGestures) {
+ Map<InputGestureData.Trigger, InputGestureData> customGestures =
+ mCustomInputGestures.get(userId);
+ if (customGestures == null) {
+ return null;
+ }
+ int modifierState = event.getMetaState() & KEY_GESTURE_META_MASK;
+ return customGestures.get(InputGestureData.createKeyTrigger(keyCode, modifierState));
+ }
+ }
+
public void dump(IndentingPrintWriter ipw) {
ipw.println("InputGestureManager:");
ipw.increaseIndent();
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index d43ce71898d3..26929f5999eb 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -3022,6 +3022,7 @@ public class InputManagerService extends IInputManager.Stub
private void handleCurrentUserChanged(@UserIdInt int userId) {
mCurrentUserId = userId;
+ mKeyGestureController.setCurrentUserId(userId);
}
/**
diff --git a/services/core/java/com/android/server/input/InputShellCommand.java b/services/core/java/com/android/server/input/InputShellCommand.java
index 4c5a3c27e156..98330163a693 100644
--- a/services/core/java/com/android/server/input/InputShellCommand.java
+++ b/services/core/java/com/android/server/input/InputShellCommand.java
@@ -89,6 +89,9 @@ public class InputShellCommand extends ShellCommand {
private static final int DEFAULT_FLAGS = 0;
private static final boolean INJECT_ASYNC = true;
private static final boolean INJECT_SYNC = false;
+ private static final long SECOND_IN_MILLISECONDS = 1000;
+
+ public static final int SWIPE_EVENT_HZ_DEFAULT = 120;
/** Modifier key to meta state */
private static final Map<Integer, Integer> MODIFIER;
@@ -519,11 +522,30 @@ public class InputShellCommand extends ShellCommand {
}
long now = SystemClock.uptimeMillis();
final long endTime = down + duration;
+ final float swipeEventPeriodMillis =
+ (float) SECOND_IN_MILLISECONDS / SWIPE_EVENT_HZ_DEFAULT;
+ int injected = 1;
while (now < endTime) {
- final long elapsedTime = now - down;
+ // Ensure that we inject at most at the frequency of SWIPE_EVENT_HZ_DEFAULT
+ // by waiting an additional delta between the actual time and expected time.
+ long elapsedTime = now - down;
+ final long errorMillis =
+ (long) Math.floor(injected * swipeEventPeriodMillis - elapsedTime);
+ if (errorMillis > 0) {
+ // Make sure not to exceed the duration and inject an extra event.
+ if (errorMillis > endTime - now) {
+ sleep(endTime - now);
+ break;
+ }
+ sleep(errorMillis);
+ }
+
+ now = SystemClock.uptimeMillis();
+ elapsedTime = now - down;
final float alpha = (float) elapsedTime / duration;
injectMotionEvent(inputSource, MotionEvent.ACTION_MOVE, down, now,
lerp(x1, x2, alpha), lerp(y1, y2, alpha), 1.0f, displayId);
+ injected++;
now = SystemClock.uptimeMillis();
}
injectMotionEvent(inputSource, MotionEvent.ACTION_UP, down, now, x2, y2, 0.0f,
diff --git a/services/core/java/com/android/server/input/KeyGestureController.java b/services/core/java/com/android/server/input/KeyGestureController.java
index 68eaf7242d04..ebeef651c19b 100644
--- a/services/core/java/com/android/server/input/KeyGestureController.java
+++ b/services/core/java/com/android/server/input/KeyGestureController.java
@@ -25,6 +25,7 @@ import static com.android.hardware.input.Flags.useKeyGestureEventHandler;
import static com.android.hardware.input.Flags.useKeyGestureEventHandlerMultiPressGestures;
import static com.android.server.flags.Flags.newBugreportKeyboardShortcut;
import static com.android.window.flags.Flags.enableMoveToNextDisplayShortcut;
+import static com.android.window.flags.Flags.enableTaskResizingKeyboardShortcuts;
import android.annotation.BinderThread;
import android.annotation.MainThread;
@@ -39,6 +40,7 @@ import android.content.res.Resources;
import android.database.ContentObserver;
import android.hardware.input.AidlInputGestureData;
import android.hardware.input.AidlKeyGestureEvent;
+import android.hardware.input.AppLaunchData;
import android.hardware.input.IKeyGestureEventListener;
import android.hardware.input.IKeyGestureHandler;
import android.hardware.input.InputGestureData;
@@ -90,6 +92,9 @@ final class KeyGestureController {
// Maximum key gesture events that are tracked and will be available in input dump.
private static final int MAX_TRACKED_EVENTS = 10;
+ private static final int SHORTCUT_META_MASK =
+ KeyEvent.META_META_ON | KeyEvent.META_CTRL_ON | KeyEvent.META_ALT_ON
+ | KeyEvent.META_SHIFT_ON;
private static final int MSG_NOTIFY_KEY_GESTURE_EVENT = 1;
@@ -115,6 +120,10 @@ final class KeyGestureController {
private final KeyCombinationManager mKeyCombinationManager;
private final SettingsObserver mSettingsObserver;
private final InputGestureManager mInputGestureManager = new InputGestureManager();
+ private static final Object mUserLock = new Object();
+ @UserIdInt
+ @GuardedBy("mUserLock")
+ private int mCurrentUserId = UserHandle.USER_SYSTEM;
// Pending actions
private boolean mPendingMetaAction;
@@ -228,7 +237,7 @@ final class KeyGestureController {
@Override
public void execute() {
- handleKeyGesture(
+ handleMultiKeyGesture(
new int[]{KeyEvent.KEYCODE_VOLUME_DOWN, KeyEvent.KEYCODE_POWER},
KeyGestureEvent.KEY_GESTURE_TYPE_SCREENSHOT_CHORD,
KeyGestureEvent.ACTION_GESTURE_START, 0);
@@ -236,7 +245,7 @@ final class KeyGestureController {
@Override
public void cancel() {
- handleKeyGesture(
+ handleMultiKeyGesture(
new int[]{KeyEvent.KEYCODE_VOLUME_DOWN, KeyEvent.KEYCODE_POWER},
KeyGestureEvent.KEY_GESTURE_TYPE_SCREENSHOT_CHORD,
KeyGestureEvent.ACTION_GESTURE_COMPLETE,
@@ -256,14 +265,14 @@ final class KeyGestureController {
@Override
public void execute() {
- handleKeyGesture(new int[]{KeyEvent.KEYCODE_POWER,
+ handleMultiKeyGesture(new int[]{KeyEvent.KEYCODE_POWER,
KeyEvent.KEYCODE_STEM_PRIMARY},
KeyGestureEvent.KEY_GESTURE_TYPE_SCREENSHOT_CHORD,
KeyGestureEvent.ACTION_GESTURE_START, 0);
}
@Override
public void cancel() {
- handleKeyGesture(new int[]{KeyEvent.KEYCODE_POWER,
+ handleMultiKeyGesture(new int[]{KeyEvent.KEYCODE_POWER,
KeyEvent.KEYCODE_STEM_PRIMARY},
KeyGestureEvent.KEY_GESTURE_TYPE_SCREENSHOT_CHORD,
KeyGestureEvent.ACTION_GESTURE_COMPLETE,
@@ -284,7 +293,7 @@ final class KeyGestureController {
@Override
public void execute() {
- handleKeyGesture(
+ handleMultiKeyGesture(
new int[]{KeyEvent.KEYCODE_VOLUME_DOWN, KeyEvent.KEYCODE_VOLUME_UP},
KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_SHORTCUT_CHORD,
KeyGestureEvent.ACTION_GESTURE_START, 0);
@@ -292,7 +301,7 @@ final class KeyGestureController {
@Override
public void cancel() {
- handleKeyGesture(
+ handleMultiKeyGesture(
new int[]{KeyEvent.KEYCODE_VOLUME_DOWN, KeyEvent.KEYCODE_VOLUME_UP},
KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_SHORTCUT_CHORD,
KeyGestureEvent.ACTION_GESTURE_COMPLETE,
@@ -324,7 +333,7 @@ final class KeyGestureController {
if (gestureType == KeyGestureEvent.KEY_GESTURE_TYPE_UNSPECIFIED) {
return;
}
- handleKeyGesture(
+ handleMultiKeyGesture(
new int[]{KeyEvent.KEYCODE_VOLUME_UP, KeyEvent.KEYCODE_POWER},
gestureType, KeyGestureEvent.ACTION_GESTURE_START, 0);
}
@@ -334,7 +343,7 @@ final class KeyGestureController {
if (gestureType == KeyGestureEvent.KEY_GESTURE_TYPE_UNSPECIFIED) {
return;
}
- handleKeyGesture(
+ handleMultiKeyGesture(
new int[]{KeyEvent.KEYCODE_VOLUME_UP, KeyEvent.KEYCODE_POWER},
gestureType, KeyGestureEvent.ACTION_GESTURE_COMPLETE,
KeyGestureEvent.FLAG_CANCELLED);
@@ -368,7 +377,7 @@ final class KeyGestureController {
@Override
public void execute() {
- handleKeyGesture(
+ handleMultiKeyGesture(
new int[]{KeyEvent.KEYCODE_BACK, KeyEvent.KEYCODE_DPAD_DOWN},
KeyGestureEvent.KEY_GESTURE_TYPE_TV_ACCESSIBILITY_SHORTCUT_CHORD,
KeyGestureEvent.ACTION_GESTURE_START, 0);
@@ -376,7 +385,7 @@ final class KeyGestureController {
@Override
public void cancel() {
- handleKeyGesture(
+ handleMultiKeyGesture(
new int[]{KeyEvent.KEYCODE_BACK, KeyEvent.KEYCODE_DPAD_DOWN},
KeyGestureEvent.KEY_GESTURE_TYPE_TV_ACCESSIBILITY_SHORTCUT_CHORD,
KeyGestureEvent.ACTION_GESTURE_COMPLETE,
@@ -405,14 +414,14 @@ final class KeyGestureController {
@Override
public void execute() {
- handleKeyGesture(
+ handleMultiKeyGesture(
new int[]{KeyEvent.KEYCODE_BACK, KeyEvent.KEYCODE_DPAD_CENTER},
KeyGestureEvent.KEY_GESTURE_TYPE_TV_TRIGGER_BUG_REPORT,
KeyGestureEvent.ACTION_GESTURE_START, 0);
}
@Override
public void cancel() {
- handleKeyGesture(
+ handleMultiKeyGesture(
new int[]{KeyEvent.KEYCODE_BACK, KeyEvent.KEYCODE_DPAD_CENTER},
KeyGestureEvent.KEY_GESTURE_TYPE_TV_TRIGGER_BUG_REPORT,
KeyGestureEvent.ACTION_GESTURE_COMPLETE,
@@ -487,7 +496,7 @@ final class KeyGestureController {
private boolean interceptSystemKeysAndShortcuts(IBinder focusedToken, KeyEvent event) {
final int keyCode = event.getKeyCode();
final int repeatCount = event.getRepeatCount();
- final int metaState = event.getMetaState();
+ final int metaState = event.getMetaState() & SHORTCUT_META_MASK;
final boolean down = event.getAction() == KeyEvent.ACTION_DOWN;
final boolean canceled = event.isCanceled();
final int displayId = event.getDisplayId();
@@ -510,7 +519,7 @@ final class KeyGestureController {
return handleKeyGesture(deviceId, new int[]{keyCode}, KeyEvent.META_META_ON,
KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_ASSISTANT,
KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
- focusedToken, /* flags = */0);
+ focusedToken, /* flags = */0, /* appLaunchData = */null);
}
break;
case KeyEvent.KEYCODE_RECENT_APPS:
@@ -518,7 +527,7 @@ final class KeyGestureController {
handleKeyGesture(deviceId, new int[]{keyCode}, /* modifierState = */0,
KeyGestureEvent.KEY_GESTURE_TYPE_RECENT_APPS,
KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
- focusedToken, /* flags = */0);
+ focusedToken, /* flags = */0, /* appLaunchData = */null);
}
return true;
case KeyEvent.KEYCODE_APP_SWITCH:
@@ -526,12 +535,13 @@ final class KeyGestureController {
handleKeyGesture(deviceId, new int[]{keyCode}, /* modifierState = */0,
KeyGestureEvent.KEY_GESTURE_TYPE_APP_SWITCH,
KeyGestureEvent.ACTION_GESTURE_START, displayId,
- focusedToken, /* flags = */0);
+ focusedToken, /* flags = */0, /* appLaunchData = */null);
} else if (!down) {
handleKeyGesture(deviceId, new int[]{keyCode}, /* modifierState = */0,
KeyGestureEvent.KEY_GESTURE_TYPE_APP_SWITCH,
KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
- focusedToken, canceled ? KeyGestureEvent.FLAG_CANCELLED : 0);
+ focusedToken, canceled ? KeyGestureEvent.FLAG_CANCELLED : 0,
+ /* appLaunchData = */null);
}
return true;
case KeyEvent.KEYCODE_H:
@@ -540,7 +550,7 @@ final class KeyGestureController {
return handleKeyGesture(deviceId, new int[]{keyCode}, KeyEvent.META_META_ON,
KeyGestureEvent.KEY_GESTURE_TYPE_HOME,
KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
- focusedToken, /* flags = */0);
+ focusedToken, /* flags = */0, /* appLaunchData = */null);
}
break;
case KeyEvent.KEYCODE_I:
@@ -548,7 +558,7 @@ final class KeyGestureController {
return handleKeyGesture(deviceId, new int[]{keyCode}, KeyEvent.META_META_ON,
KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_SYSTEM_SETTINGS,
KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
- focusedToken, /* flags = */0);
+ focusedToken, /* flags = */0, /* appLaunchData = */null);
}
break;
case KeyEvent.KEYCODE_L:
@@ -556,7 +566,7 @@ final class KeyGestureController {
return handleKeyGesture(deviceId, new int[]{keyCode}, KeyEvent.META_META_ON,
KeyGestureEvent.KEY_GESTURE_TYPE_LOCK_SCREEN,
KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
- focusedToken, /* flags = */0);
+ focusedToken, /* flags = */0, /* appLaunchData = */null);
}
break;
case KeyEvent.KEYCODE_N:
@@ -566,13 +576,13 @@ final class KeyGestureController {
KeyEvent.META_META_ON | KeyEvent.META_CTRL_ON,
KeyGestureEvent.KEY_GESTURE_TYPE_OPEN_NOTES,
KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
- focusedToken, /* flags = */0);
+ focusedToken, /* flags = */0, /* appLaunchData = */null);
} else {
return handleKeyGesture(deviceId, new int[]{keyCode},
KeyEvent.META_META_ON,
KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL,
KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
- focusedToken, /* flags = */0);
+ focusedToken, /* flags = */0, /* appLaunchData = */null);
}
}
break;
@@ -582,7 +592,7 @@ final class KeyGestureController {
KeyEvent.META_META_ON | KeyEvent.META_CTRL_ON,
KeyGestureEvent.KEY_GESTURE_TYPE_TAKE_SCREENSHOT,
KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
- focusedToken, /* flags = */0);
+ focusedToken, /* flags = */0, /* appLaunchData = */null);
}
break;
case KeyEvent.KEYCODE_T:
@@ -592,7 +602,7 @@ final class KeyGestureController {
KeyEvent.META_META_ON | KeyEvent.META_ALT_ON,
KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_TALKBACK,
KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
- focusedToken, /* flags = */0);
+ focusedToken, /* flags = */0, /* appLaunchData = */null);
}
}
break;
@@ -604,7 +614,7 @@ final class KeyGestureController {
KeyEvent.META_META_ON | KeyEvent.META_ALT_ON,
KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_BOUNCE_KEYS,
KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
- focusedToken, /* flags = */0);
+ focusedToken, /* flags = */0, /* appLaunchData = */null);
}
}
break;
@@ -616,7 +626,7 @@ final class KeyGestureController {
KeyEvent.META_META_ON | KeyEvent.META_ALT_ON,
KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_MOUSE_KEYS,
KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
- focusedToken, /* flags = */0);
+ focusedToken, /* flags = */0, /* appLaunchData = */null);
}
}
break;
@@ -628,7 +638,7 @@ final class KeyGestureController {
KeyEvent.META_META_ON | KeyEvent.META_ALT_ON,
KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_STICKY_KEYS,
KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
- focusedToken, /* flags = */0);
+ focusedToken, /* flags = */0, /* appLaunchData = */null);
}
}
break;
@@ -640,7 +650,7 @@ final class KeyGestureController {
KeyEvent.META_META_ON | KeyEvent.META_ALT_ON,
KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_SLOW_KEYS,
KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
- focusedToken, /* flags = */0);
+ focusedToken, /* flags = */0, /* appLaunchData = */null);
}
}
break;
@@ -652,7 +662,7 @@ final class KeyGestureController {
KeyEvent.META_META_ON | KeyEvent.META_CTRL_ON,
KeyGestureEvent.KEY_GESTURE_TYPE_TRIGGER_BUG_REPORT,
KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
- focusedToken, /* flags = */0);
+ focusedToken, /* flags = */0, /* appLaunchData = */null);
}
}
// fall through
@@ -661,7 +671,7 @@ final class KeyGestureController {
return handleKeyGesture(deviceId, new int[]{keyCode}, KeyEvent.META_META_ON,
KeyGestureEvent.KEY_GESTURE_TYPE_BACK,
KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
- focusedToken, /* flags = */0);
+ focusedToken, /* flags = */0, /* appLaunchData = */null);
}
break;
case KeyEvent.KEYCODE_DPAD_UP:
@@ -670,7 +680,7 @@ final class KeyGestureController {
KeyEvent.META_META_ON | KeyEvent.META_CTRL_ON,
KeyGestureEvent.KEY_GESTURE_TYPE_MULTI_WINDOW_NAVIGATION,
KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
- focusedToken, /* flags = */0);
+ focusedToken, /* flags = */0, /* appLaunchData = */null);
}
break;
case KeyEvent.KEYCODE_DPAD_DOWN:
@@ -679,7 +689,7 @@ final class KeyGestureController {
KeyEvent.META_META_ON | KeyEvent.META_CTRL_ON,
KeyGestureEvent.KEY_GESTURE_TYPE_DESKTOP_MODE,
KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
- focusedToken, /* flags = */0);
+ focusedToken, /* flags = */0, /* appLaunchData = */null);
}
break;
case KeyEvent.KEYCODE_DPAD_LEFT:
@@ -689,19 +699,19 @@ final class KeyGestureController {
KeyEvent.META_META_ON | KeyEvent.META_CTRL_ON,
KeyGestureEvent.KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_LEFT,
KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
- focusedToken, /* flags = */0);
+ focusedToken, /* flags = */0, /* appLaunchData = */null);
} else if (event.isAltPressed()) {
return handleKeyGesture(deviceId, new int[]{keyCode},
KeyEvent.META_META_ON | KeyEvent.META_ALT_ON,
KeyGestureEvent.KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_LEFT,
KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
- focusedToken, /* flags = */0);
+ focusedToken, /* flags = */0, /* appLaunchData = */null);
} else {
return handleKeyGesture(deviceId, new int[]{keyCode},
KeyEvent.META_META_ON,
KeyGestureEvent.KEY_GESTURE_TYPE_BACK,
KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
- focusedToken, /* flags = */0);
+ focusedToken, /* flags = */0, /* appLaunchData = */null);
}
}
break;
@@ -712,13 +722,13 @@ final class KeyGestureController {
KeyEvent.META_META_ON | KeyEvent.META_CTRL_ON,
KeyGestureEvent.KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_RIGHT,
KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
- focusedToken, /* flags = */0);
+ focusedToken, /* flags = */0, /* appLaunchData = */null);
} else if (event.isAltPressed()) {
return handleKeyGesture(deviceId, new int[]{keyCode},
KeyEvent.META_META_ON | KeyEvent.META_ALT_ON,
KeyGestureEvent.KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_RIGHT,
KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
- focusedToken, /* flags = */0);
+ focusedToken, /* flags = */0, /* appLaunchData = */null);
}
}
break;
@@ -729,8 +739,51 @@ final class KeyGestureController {
KeyEvent.META_META_ON | KeyEvent.META_CTRL_ON,
KeyGestureEvent.KEY_GESTURE_TYPE_MOVE_TO_NEXT_DISPLAY,
KeyGestureEvent.ACTION_GESTURE_COMPLETE,
- displayId,
- focusedToken, /* flags = */0);
+ displayId, focusedToken, /* flags = */0, /* appLaunchData = */null);
+ }
+ }
+ break;
+ case KeyEvent.KEYCODE_LEFT_BRACKET:
+ if (enableTaskResizingKeyboardShortcuts()) {
+ if (firstDown && event.isAltPressed()) {
+ return handleKeyGesture(deviceId, new int[]{keyCode},
+ KeyEvent.META_ALT_ON,
+ KeyGestureEvent.KEY_GESTURE_TYPE_SNAP_LEFT_FREEFORM_WINDOW,
+ KeyGestureEvent.ACTION_GESTURE_COMPLETE,
+ displayId, focusedToken, /* flags = */0, /* appLaunchData = */null);
+ }
+ }
+ break;
+ case KeyEvent.KEYCODE_RIGHT_BRACKET:
+ if (enableTaskResizingKeyboardShortcuts()) {
+ if (firstDown && event.isAltPressed()) {
+ return handleKeyGesture(deviceId, new int[]{keyCode},
+ KeyEvent.META_ALT_ON,
+ KeyGestureEvent.KEY_GESTURE_TYPE_SNAP_RIGHT_FREEFORM_WINDOW,
+ KeyGestureEvent.ACTION_GESTURE_COMPLETE,
+ displayId, focusedToken, /* flags = */0, /* appLaunchData = */null);
+ }
+ }
+ break;
+ case KeyEvent.KEYCODE_EQUALS:
+ if (enableTaskResizingKeyboardShortcuts()) {
+ if (firstDown && event.isAltPressed()) {
+ return handleKeyGesture(deviceId, new int[]{keyCode},
+ KeyEvent.META_ALT_ON,
+ KeyGestureEvent.KEY_GESTURE_TYPE_MAXIMIZE_FREEFORM_WINDOW,
+ KeyGestureEvent.ACTION_GESTURE_COMPLETE,
+ displayId, focusedToken, /* flags = */0, /* appLaunchData = */null);
+ }
+ }
+ break;
+ case KeyEvent.KEYCODE_MINUS:
+ if (enableTaskResizingKeyboardShortcuts()) {
+ if (firstDown && event.isAltPressed()) {
+ return handleKeyGesture(deviceId, new int[]{keyCode},
+ KeyEvent.META_ALT_ON,
+ KeyGestureEvent.KEY_GESTURE_TYPE_RESTORE_FREEFORM_WINDOW_SIZE,
+ KeyGestureEvent.ACTION_GESTURE_COMPLETE,
+ displayId, focusedToken, /* flags = */0, /* appLaunchData = */null);
}
}
break;
@@ -739,7 +792,7 @@ final class KeyGestureController {
return handleKeyGesture(deviceId, new int[]{keyCode}, KeyEvent.META_META_ON,
KeyGestureEvent.KEY_GESTURE_TYPE_OPEN_SHORTCUT_HELPER,
KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
- focusedToken, /* flags = */0);
+ focusedToken, /* flags = */0, /* appLaunchData = */null);
}
break;
case KeyEvent.KEYCODE_BRIGHTNESS_UP:
@@ -750,7 +803,7 @@ final class KeyGestureController {
? KeyGestureEvent.KEY_GESTURE_TYPE_BRIGHTNESS_UP
: KeyGestureEvent.KEY_GESTURE_TYPE_BRIGHTNESS_DOWN,
KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
- focusedToken, /* flags = */0);
+ focusedToken, /* flags = */0, /* appLaunchData = */null);
}
return true;
case KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_DOWN:
@@ -758,7 +811,7 @@ final class KeyGestureController {
handleKeyGesture(deviceId, new int[]{keyCode}, /* modifierState = */0,
KeyGestureEvent.KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_DOWN,
KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
- focusedToken, /* flags = */0);
+ focusedToken, /* flags = */0, /* appLaunchData = */null);
}
return true;
case KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_UP:
@@ -766,7 +819,7 @@ final class KeyGestureController {
handleKeyGesture(deviceId, new int[]{keyCode}, /* modifierState = */0,
KeyGestureEvent.KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_UP,
KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
- focusedToken, /* flags = */0);
+ focusedToken, /* flags = */0, /* appLaunchData = */null);
}
return true;
case KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_TOGGLE:
@@ -775,7 +828,7 @@ final class KeyGestureController {
handleKeyGesture(deviceId, new int[]{keyCode}, /* modifierState = */0,
KeyGestureEvent.KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_TOGGLE,
KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
- focusedToken, /* flags = */0);
+ focusedToken, /* flags = */0, /* appLaunchData = */null);
}
return true;
case KeyEvent.KEYCODE_ALL_APPS:
@@ -783,7 +836,7 @@ final class KeyGestureController {
handleKeyGesture(deviceId, new int[]{keyCode}, /* modifierState = */0,
KeyGestureEvent.KEY_GESTURE_TYPE_ALL_APPS,
KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
- focusedToken, /* flags = */0);
+ focusedToken, /* flags = */0, /* appLaunchData = */null);
}
return true;
case KeyEvent.KEYCODE_NOTIFICATION:
@@ -791,7 +844,7 @@ final class KeyGestureController {
handleKeyGesture(deviceId, new int[]{keyCode}, /* modifierState = */0,
KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL,
KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
- focusedToken, /* flags = */0);
+ focusedToken, /* flags = */0, /* appLaunchData = */null);
}
return true;
case KeyEvent.KEYCODE_SEARCH:
@@ -799,7 +852,7 @@ final class KeyGestureController {
return handleKeyGesture(deviceId, new int[]{keyCode}, /* modifierState = */0,
KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_SEARCH,
KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
- focusedToken, /* flags = */0);
+ focusedToken, /* flags = */0, /* appLaunchData = */null);
}
break;
@@ -810,13 +863,13 @@ final class KeyGestureController {
new int[]{keyCode}, /* modifierState = */0,
KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_SYSTEM_SETTINGS,
KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
- focusedToken, /* flags = */0);
+ focusedToken, /* flags = */0, /* appLaunchData = */null);
} else if (mSettingsKeyBehavior == SETTINGS_KEY_BEHAVIOR_NOTIFICATION_PANEL) {
handleKeyGesture(deviceId,
new int[]{keyCode}, /* modifierState = */0,
KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL,
KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
- focusedToken, /* flags = */0);
+ focusedToken, /* flags = */0, /* appLaunchData = */null);
}
}
return true;
@@ -826,7 +879,7 @@ final class KeyGestureController {
event.isShiftPressed() ? KeyEvent.META_SHIFT_ON : 0,
KeyGestureEvent.KEY_GESTURE_TYPE_LANGUAGE_SWITCH,
KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
- focusedToken, /* flags = */0);
+ focusedToken, /* flags = */0, /* appLaunchData = */null);
}
return true;
case KeyEvent.KEYCODE_CAPS_LOCK:
@@ -836,7 +889,8 @@ final class KeyGestureController {
AidlKeyGestureEvent eventToNotify = createKeyGestureEvent(deviceId,
new int[]{keyCode}, metaState,
KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_CAPS_LOCK,
- KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId, /* flags = */0);
+ KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId, /* flags = */0,
+ /* appLaunchData = */null);
Message msg = Message.obtain(mHandler, MSG_NOTIFY_KEY_GESTURE_EVENT,
eventToNotify);
mHandler.sendMessage(msg);
@@ -847,7 +901,7 @@ final class KeyGestureController {
handleKeyGesture(deviceId, new int[]{keyCode}, /* modifierState = */0,
KeyGestureEvent.KEY_GESTURE_TYPE_TAKE_SCREENSHOT,
KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
- focusedToken, /* flags = */0);
+ focusedToken, /* flags = */0, /* appLaunchData = */null);
}
return true;
case KeyEvent.KEYCODE_META_LEFT:
@@ -868,7 +922,7 @@ final class KeyGestureController {
KeyEvent.KEYCODE_ALT_LEFT}, /* modifierState = */0,
KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_CAPS_LOCK,
KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
- focusedToken, /* flags = */0);
+ focusedToken, /* flags = */0, /* appLaunchData = */null);
} else if (mPendingMetaAction) {
mPendingMetaAction = false;
@@ -877,7 +931,7 @@ final class KeyGestureController {
/* modifierState = */0,
KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_ALL_APPS,
KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
- focusedToken, /* flags = */0);
+ focusedToken, /* flags = */0, /* appLaunchData = */null);
}
}
}
@@ -888,7 +942,7 @@ final class KeyGestureController {
return handleKeyGesture(deviceId, new int[]{keyCode}, KeyEvent.META_META_ON,
KeyGestureEvent.KEY_GESTURE_TYPE_RECENT_APPS,
KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
- focusedToken, /* flags = */0);
+ focusedToken, /* flags = */0, /* appLaunchData = */null);
} else if (!mPendingHideRecentSwitcher) {
final int shiftlessModifiers =
event.getModifiers() & ~KeyEvent.META_SHIFT_MASK;
@@ -899,7 +953,7 @@ final class KeyGestureController {
KeyEvent.META_ALT_ON,
KeyGestureEvent.KEY_GESTURE_TYPE_RECENT_APPS_SWITCHER,
KeyGestureEvent.ACTION_GESTURE_START, displayId,
- focusedToken, /* flags = */0);
+ focusedToken, /* flags = */0, /* appLaunchData = */null);
}
}
}
@@ -920,7 +974,7 @@ final class KeyGestureController {
KeyEvent.META_ALT_ON,
KeyGestureEvent.KEY_GESTURE_TYPE_RECENT_APPS_SWITCHER,
KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
- focusedToken, /* flags = */0);
+ focusedToken, /* flags = */0, /* appLaunchData = */null);
}
// Toggle Caps Lock on META-ALT.
@@ -930,7 +984,7 @@ final class KeyGestureController {
KeyEvent.KEYCODE_ALT_LEFT}, /* modifierState = */0,
KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_CAPS_LOCK,
KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
- focusedToken, /* flags = */0);
+ focusedToken, /* flags = */0, /* appLaunchData = */null);
}
}
break;
@@ -949,6 +1003,22 @@ final class KeyGestureController {
+ " interceptKeyBeforeQueueing");
return true;
}
+
+ if (firstDown) {
+ InputGestureData customGesture;
+ synchronized (mUserLock) {
+ customGesture = mInputGestureManager.getCustomGestureForKeyEvent(mCurrentUserId,
+ event);
+ }
+ if (customGesture == null) {
+ return false;
+ }
+ return handleKeyGesture(deviceId, new int[]{keyCode}, metaState,
+ customGesture.getAction().keyGestureType(),
+ KeyGestureEvent.ACTION_GESTURE_COMPLETE,
+ displayId, focusedToken, /* flags = */0,
+ customGesture.getAction().appLaunchData());
+ }
return false;
}
@@ -971,7 +1041,7 @@ final class KeyGestureController {
? KeyEvent.META_SHIFT_ON : 0),
KeyGestureEvent.KEY_GESTURE_TYPE_LANGUAGE_SWITCH,
KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
- focusedToken, /* flags = */0);
+ focusedToken, /* flags = */0, /* appLaunchData = */null);
}
}
break;
@@ -983,7 +1053,7 @@ final class KeyGestureController {
KeyEvent.META_CTRL_ON | KeyEvent.META_ALT_ON,
KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_SHORTCUT,
KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
- focusedToken, /* flags = */0);
+ focusedToken, /* flags = */0, /* appLaunchData = */null);
}
break;
case KeyEvent.KEYCODE_SYSRQ:
@@ -991,7 +1061,7 @@ final class KeyGestureController {
return handleKeyGesture(deviceId, new int[]{keyCode}, /* modifierState = */0,
KeyGestureEvent.KEY_GESTURE_TYPE_TAKE_SCREENSHOT,
KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
- focusedToken, /* flags = */0);
+ focusedToken, /* flags = */0, /* appLaunchData = */null);
}
break;
case KeyEvent.KEYCODE_ESCAPE:
@@ -999,7 +1069,7 @@ final class KeyGestureController {
return handleKeyGesture(deviceId, new int[]{keyCode}, /* modifierState = */0,
KeyGestureEvent.KEY_GESTURE_TYPE_CLOSE_ALL_DIALOGS,
KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
- focusedToken, /* flags = */0);
+ focusedToken, /* flags = */0, /* appLaunchData = */null);
}
break;
}
@@ -1007,18 +1077,19 @@ final class KeyGestureController {
return false;
}
- private boolean handleKeyGesture(int[] keycodes,
+ private void handleMultiKeyGesture(int[] keycodes,
@KeyGestureEvent.KeyGestureType int gestureType, int action, int flags) {
- return handleKeyGesture(KeyCharacterMap.VIRTUAL_KEYBOARD, keycodes, /* modifierState= */0,
- gestureType, action, Display.DEFAULT_DISPLAY, /* focusedToken = */null, flags);
+ handleKeyGesture(KeyCharacterMap.VIRTUAL_KEYBOARD, keycodes, /* modifierState= */0,
+ gestureType, action, Display.DEFAULT_DISPLAY, /* focusedToken = */null, flags,
+ /* appLaunchData = */null);
}
@VisibleForTesting
boolean handleKeyGesture(int deviceId, int[] keycodes, int modifierState,
@KeyGestureEvent.KeyGestureType int gestureType, int action, int displayId,
- @Nullable IBinder focusedToken, int flags) {
+ @Nullable IBinder focusedToken, int flags, @Nullable AppLaunchData appLaunchData) {
return handleKeyGesture(createKeyGestureEvent(deviceId, keycodes,
- modifierState, gestureType, action, displayId, flags), focusedToken);
+ modifierState, gestureType, action, displayId, flags, appLaunchData), focusedToken);
}
private boolean handleKeyGesture(AidlKeyGestureEvent event, @Nullable IBinder focusedToken) {
@@ -1050,18 +1121,27 @@ final class KeyGestureController {
// TODO(b/358569822): Once we move the gesture detection logic to IMS, we ideally
// should not rely on PWM to tell us about the gesture start and end.
AidlKeyGestureEvent event = createKeyGestureEvent(deviceId, keycodes, modifierState,
- gestureType, KeyGestureEvent.ACTION_GESTURE_COMPLETE, Display.DEFAULT_DISPLAY, 0);
+ gestureType, KeyGestureEvent.ACTION_GESTURE_COMPLETE, Display.DEFAULT_DISPLAY,
+ /* flags = */0, /* appLaunchData = */null);
mHandler.obtainMessage(MSG_NOTIFY_KEY_GESTURE_EVENT, event).sendToTarget();
}
public void handleKeyGesture(int deviceId, int[] keycodes, int modifierState,
@KeyGestureEvent.KeyGestureType int gestureType) {
AidlKeyGestureEvent event = createKeyGestureEvent(deviceId, keycodes, modifierState,
- gestureType, KeyGestureEvent.ACTION_GESTURE_COMPLETE, Display.DEFAULT_DISPLAY, 0);
+ gestureType, KeyGestureEvent.ACTION_GESTURE_COMPLETE, Display.DEFAULT_DISPLAY,
+ /* flags = */0, /* appLaunchData = */null);
handleKeyGesture(event, null /*focusedToken*/);
}
@MainThread
+ public void setCurrentUserId(@UserIdInt int userId) {
+ synchronized (mUserLock) {
+ mCurrentUserId = userId;
+ }
+ }
+
+ @MainThread
private void notifyKeyGestureEvent(AidlKeyGestureEvent event) {
InputDevice device = getInputDevice(event.deviceId);
if (device == null) {
@@ -1318,7 +1398,7 @@ final class KeyGestureController {
private AidlKeyGestureEvent createKeyGestureEvent(int deviceId, int[] keycodes,
int modifierState, @KeyGestureEvent.KeyGestureType int gestureType, int action,
- int displayId, int flags) {
+ int displayId, int flags, @Nullable AppLaunchData appLaunchData) {
AidlKeyGestureEvent event = new AidlKeyGestureEvent();
event.deviceId = deviceId;
event.keycodes = keycodes;
@@ -1327,6 +1407,18 @@ final class KeyGestureController {
event.action = action;
event.displayId = displayId;
event.flags = flags;
+ if (appLaunchData != null) {
+ if (appLaunchData instanceof AppLaunchData.CategoryData categoryData) {
+ event.appLaunchCategory = categoryData.getCategory();
+ } else if (appLaunchData instanceof AppLaunchData.RoleData roleData) {
+ event.appLaunchRole = roleData.getRole();
+ } else if (appLaunchData instanceof AppLaunchData.ComponentData componentData) {
+ event.appLaunchPackageName = componentData.getPackageName();
+ event.appLaunchClassName = componentData.getClassName();
+ } else {
+ throw new IllegalArgumentException("AppLaunchData type is invalid!");
+ }
+ }
return event;
}
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index 8798a64ec479..45885f0399c4 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -253,10 +253,10 @@ public class LockSettingsService extends ILockSettings.Stub {
private static final String MIGRATED_FRP2 = "migrated_frp2";
private static final String MIGRATED_KEYSTORE_NS = "migrated_keystore_namespace";
- private static final String MIGRATED_SP_CE_ONLY = "migrated_all_users_to_sp_and_bound_ce";
private static final String MIGRATED_SP_FULL = "migrated_all_users_to_sp_and_bound_keys";
private static final String MIGRATED_WEAVER_DISABLED_ON_UNSECURED_USERS =
"migrated_weaver_disabled_on_unsecured_users";
+ // Note: some other migrated_* strings used to be used and may exist in the database already.
// Duration that LockSettingsService will store the gatekeeper password for. This allows
// multiple biometric enrollments without prompting the user to enter their password via
@@ -1219,21 +1219,16 @@ public class LockSettingsService extends ILockSettings.Stub {
Slog.i(TAG, "Synthetic password is already not protected by Weaver");
}
} else if (sp == null) {
- Slogf.wtf(TAG, "Failed to unwrap synthetic password for unsecured user %d", userId);
- return;
+ throw new IllegalStateException(
+ "Failed to unwrap synthetic password for unsecured user " + userId);
}
// Call setCeStorageProtection(), to re-encrypt the CE key with the SP if it's currently
- // encrypted by an empty secret. Skip this if it was definitely already done as part of the
- // upgrade to Android 14, since while setCeStorageProtection() is idempotent it does log
- // some error messages when called again. Do not skip this if
- // config_disableWeaverOnUnsecuredUsers=true, since in that case we'd like to recover from
- // the case where an earlier upgrade to Android 14 incorrectly skipped this step.
- if (getString(MIGRATED_SP_CE_ONLY, null, 0) == null
- || isWeaverDisabledOnUnsecuredUsers()) {
- Slogf.i(TAG, "Encrypting CE key of user %d with synthetic password", userId);
- setCeStorageProtection(userId, sp);
- }
+ // encrypted by an empty secret. If the CE key is already encrypted by the SP, then this is
+ // a no-op except for some log messages.
+ Slogf.i(TAG, "Encrypting CE key of user %d with synthetic password", userId);
+ setCeStorageProtection(userId, sp);
+
Slogf.i(TAG, "Initializing Keystore super keys for user %d", userId);
initKeystoreSuperKeys(userId, sp, /* allowExisting= */ true);
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 62df825617cd..849f236916a6 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -105,6 +105,7 @@ import static android.os.UserHandle.USER_SYSTEM;
import static android.service.notification.Adjustment.KEY_TYPE;
import static android.service.notification.Adjustment.TYPE_CONTENT_RECOMMENDATION;
import static android.service.notification.Adjustment.TYPE_PROMOTION;
+import static android.service.notification.Flags.FLAG_NOTIFICATION_CONVERSATION_CHANNEL_MANAGEMENT;
import static android.service.notification.Flags.callstyleCallbackApi;
import static android.service.notification.Flags.notificationClassification;
import static android.service.notification.Flags.notificationForceGrouping;
@@ -6681,6 +6682,33 @@ public class NotificationManagerService extends SystemService {
}
@Override
+ @FlaggedApi(FLAG_NOTIFICATION_CONVERSATION_CHANNEL_MANAGEMENT)
+ public NotificationChannel createConversationNotificationChannelForPackageFromPrivilegedListener(
+ INotificationListener token, String pkg, UserHandle user,
+ String parentId, String conversationId) throws RemoteException {
+ Objects.requireNonNull(pkg);
+ Objects.requireNonNull(user);
+ Objects.requireNonNull(parentId);
+ Objects.requireNonNull(conversationId);
+
+ verifyPrivilegedListener(token, user, true);
+
+ int uid = getUidForPackageAndUser(pkg, user);
+ NotificationChannel conversationChannel =
+ mPreferencesHelper.getNotificationChannel(pkg, uid, parentId, false).copy();
+ String conversationChannelId = String.format(
+ CONVERSATION_CHANNEL_ID_FORMAT, parentId, conversationId);
+ conversationChannel.setId(conversationChannelId);
+ conversationChannel.setConversationId(parentId, conversationId);
+ createNotificationChannelsImpl(
+ pkg, uid, new ParceledListSlice(Arrays.asList(conversationChannel)));
+ handleSavePolicyFile();
+
+ return mPreferencesHelper.getConversationNotificationChannel(
+ pkg, uid, parentId, conversationId, false, false).copy();
+ }
+
+ @Override
public void updateNotificationChannelGroupFromPrivilegedListener(
INotificationListener token, String pkg, UserHandle user,
NotificationChannelGroup group) throws RemoteException {
@@ -6698,7 +6726,7 @@ public class NotificationManagerService extends SystemService {
Objects.requireNonNull(pkg);
Objects.requireNonNull(user);
- verifyPrivilegedListener(token, user, false);
+ verifyPrivilegedListener(token, user, true);
final NotificationChannel originalChannel = mPreferencesHelper.getNotificationChannel(
pkg, getUidForPackageAndUser(pkg, user), channel.getId(), true);
diff --git a/services/core/java/com/android/server/pm/BroadcastHelper.java b/services/core/java/com/android/server/pm/BroadcastHelper.java
index 84a5f2b0e8bc..9f4b9f1c1f24 100644
--- a/services/core/java/com/android/server/pm/BroadcastHelper.java
+++ b/services/core/java/com/android/server/pm/BroadcastHelper.java
@@ -64,6 +64,9 @@ import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
+import com.android.internal.pm.pkg.component.ParsedActivity;
+import com.android.internal.pm.pkg.component.ParsedProvider;
+import com.android.internal.pm.pkg.component.ParsedService;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FrameworkStatsLog;
import com.android.server.pm.pkg.AndroidPackage;
@@ -80,6 +83,8 @@ import java.util.function.BiFunction;
*/
public final class BroadcastHelper {
private static final boolean DEBUG_BROADCASTS = false;
+ private static final String PERMISSION_PACKAGE_CHANGED_BROADCAST_ON_COMPONENT_STATE_CHANGED =
+ "android.permission.INTERNAL_RECEIVE_PACKAGE_CHANGED_BROADCAST_ON_COMPONENT_STATE_CHANGED";
private final UserManagerInternal mUmInternal;
private final ActivityManagerInternal mAmInternal;
@@ -291,6 +296,57 @@ public final class BroadcastHelper {
return bOptions;
}
+ private ArrayList<String> getAllNotExportedComponents(@NonNull AndroidPackage pkg,
+ @NonNull ArrayList<String> inputComponentNames) {
+ final ArrayList<String> outputNotExportedComponentNames = new ArrayList<>();
+ int remainingComponentCount = inputComponentNames.size();
+ for (ParsedActivity component : pkg.getReceivers()) {
+ if (inputComponentNames.contains(component.getClassName())) {
+ if (!component.isExported()) {
+ outputNotExportedComponentNames.add(component.getClassName());
+ }
+ remainingComponentCount--;
+ if (remainingComponentCount <= 0) {
+ return outputNotExportedComponentNames;
+ }
+ }
+ }
+ for (ParsedProvider component : pkg.getProviders()) {
+ if (inputComponentNames.contains(component.getClassName())) {
+ if (!component.isExported()) {
+ outputNotExportedComponentNames.add(component.getClassName());
+ }
+ remainingComponentCount--;
+ if (remainingComponentCount <= 0) {
+ return outputNotExportedComponentNames;
+ }
+ }
+ }
+ for (ParsedService component : pkg.getServices()) {
+ if (inputComponentNames.contains(component.getClassName())) {
+ if (!component.isExported()) {
+ outputNotExportedComponentNames.add(component.getClassName());
+ }
+ remainingComponentCount--;
+ if (remainingComponentCount <= 0) {
+ return outputNotExportedComponentNames;
+ }
+ }
+ }
+ for (ParsedActivity component : pkg.getActivities()) {
+ if (inputComponentNames.contains(component.getClassName())) {
+ if (!component.isExported()) {
+ outputNotExportedComponentNames.add(component.getClassName());
+ }
+ remainingComponentCount--;
+ if (remainingComponentCount <= 0) {
+ return outputNotExportedComponentNames;
+ }
+ }
+ }
+ return outputNotExportedComponentNames;
+ }
+
private void sendPackageChangedBroadcastInternal(@NonNull String packageName,
boolean dontKillApp,
@NonNull ArrayList<String> componentNames,
@@ -298,10 +354,48 @@ public final class BroadcastHelper {
@Nullable String reason,
@Nullable int[] userIds,
@Nullable int[] instantUserIds,
- @Nullable SparseArray<int[]> broadcastAllowList) {
- sendPackageChangedBroadcastWithPermissions(packageName, dontKillApp, componentNames,
- packageUid, reason, userIds, instantUserIds, broadcastAllowList,
- null /* targetPackageName */, null /* requiredPermissions */);
+ @Nullable SparseArray<int[]> broadcastAllowList,
+ @NonNull AndroidPackage pkg) {
+ final boolean isForWholeApp = componentNames.contains(packageName);
+ if (isForWholeApp || !android.content.pm.Flags.reduceBroadcastsForComponentStateChanges()) {
+ sendPackageChangedBroadcastWithPermissions(packageName, dontKillApp, componentNames,
+ packageUid, reason, userIds, instantUserIds, broadcastAllowList,
+ null /* targetPackageName */, null /* requiredPermissions */);
+ return;
+ }
+ // Currently only these four components of activity, receiver, provider and service are
+ // considered to send only the broadcast to the system and the application itself when the
+ // component is not exported. In order to avoid losing to send the broadcast for other
+ // components, it gets the not exported components for these four components of activity,
+ // receiver, provider and service and the others are considered the exported components.
+ final ArrayList<String> notExportedComponentNames = getAllNotExportedComponents(pkg,
+ componentNames);
+ final ArrayList<String> exportedComponentNames = (ArrayList<String>) componentNames.clone();
+ exportedComponentNames.removeAll(notExportedComponentNames);
+
+ if (!notExportedComponentNames.isEmpty()) {
+ // Limit sending of the PACKAGE_CHANGED broadcast to only the system and the
+ // application itself when the component is not exported.
+
+ // First, send the PACKAGE_CHANGED broadcast to the system.
+ sendPackageChangedBroadcastWithPermissions(packageName, dontKillApp,
+ notExportedComponentNames, packageUid, reason, userIds, instantUserIds,
+ broadcastAllowList, "android" /* targetPackageName */,
+ new String[]{PERMISSION_PACKAGE_CHANGED_BROADCAST_ON_COMPONENT_STATE_CHANGED});
+
+ // Second, send the PACKAGE_CHANGED broadcast to the application itself.
+ sendPackageChangedBroadcastWithPermissions(packageName, dontKillApp,
+ notExportedComponentNames, packageUid, reason, userIds, instantUserIds,
+ broadcastAllowList, packageName /* targetPackageName */,
+ null /* requiredPermissions */);
+ }
+
+ if (!exportedComponentNames.isEmpty()) {
+ sendPackageChangedBroadcastWithPermissions(packageName, dontKillApp,
+ exportedComponentNames, packageUid, reason, userIds, instantUserIds,
+ broadcastAllowList, null /* targetPackageName */,
+ null /* requiredPermissions */);
+ }
}
private void sendPackageChangedBroadcastWithPermissions(@NonNull String packageName,
@@ -830,7 +924,7 @@ public final class BroadcastHelper {
@NonNull String reason) {
PackageStateInternal setting = snapshot.getPackageStateInternal(packageName,
Process.SYSTEM_UID);
- if (setting == null) {
+ if (setting == null || setting.getPkg() == null) {
return;
}
final int userId = UserHandle.getUserId(packageUid);
@@ -842,7 +936,7 @@ public final class BroadcastHelper {
isInstantApp ? null : snapshot.getVisibilityAllowLists(packageName, userIds);
mHandler.post(() -> sendPackageChangedBroadcastInternal(
packageName, dontKillApp, componentNames, packageUid, reason, userIds,
- instantUserIds, broadcastAllowList));
+ instantUserIds, broadcastAllowList, setting.getPkg()));
mPackageMonitorCallbackHelper.notifyPackageChanged(packageName, dontKillApp, componentNames,
packageUid, reason, userIds, instantUserIds, broadcastAllowList, mHandler);
}
diff --git a/services/core/java/com/android/server/security/advancedprotection/AdvancedProtectionService.java b/services/core/java/com/android/server/security/advancedprotection/AdvancedProtectionService.java
index c33ed555a7f9..1260eeec098f 100644
--- a/services/core/java/com/android/server/security/advancedprotection/AdvancedProtectionService.java
+++ b/services/core/java/com/android/server/security/advancedprotection/AdvancedProtectionService.java
@@ -33,6 +33,7 @@ import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ShellCallback;
import android.provider.Settings;
+import android.security.advancedprotection.AdvancedProtectionFeature;
import android.security.advancedprotection.IAdvancedProtectionCallback;
import android.security.advancedprotection.IAdvancedProtectionService;
import android.util.ArrayMap;
@@ -44,9 +45,11 @@ import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.pm.UserManagerInternal;
import com.android.server.security.advancedprotection.features.AdvancedProtectionHook;
+import com.android.server.security.advancedprotection.features.AdvancedProtectionProvider;
import java.io.FileDescriptor;
import java.util.ArrayList;
+import java.util.List;
/** @hide */
public class AdvancedProtectionService extends IAdvancedProtectionService.Stub {
@@ -58,10 +61,12 @@ public class AdvancedProtectionService extends IAdvancedProtectionService.Stub
private final Handler mHandler;
private final AdvancedProtectionStore mStore;
- // Features owned by the service - their code will be executed when state changes
+ // Features living with the service - their code will be executed when state changes
private final ArrayList<AdvancedProtectionHook> mHooks = new ArrayList<>();
// External features - they will be called on state change
private final ArrayMap<IBinder, IAdvancedProtectionCallback> mCallbacks = new ArrayMap<>();
+ // For tracking only - not called on state change
+ private final ArrayList<AdvancedProtectionProvider> mProviders = new ArrayList<>();
private AdvancedProtectionService(@NonNull Context context) {
super(PermissionEnforcer.fromContext(context));
@@ -71,13 +76,17 @@ public class AdvancedProtectionService extends IAdvancedProtectionService.Stub
}
private void initFeatures(boolean enabled) {
+ // Empty until features are added.
+ // Examples:
+ // mHooks.add(new SideloadingAdvancedProtectionHook(mContext, enabled));
+ // mProviders.add(new WifiAdvancedProtectionProvider());
}
// Only for tests
@VisibleForTesting
AdvancedProtectionService(@NonNull Context context, @NonNull AdvancedProtectionStore store,
@NonNull Looper looper, @NonNull PermissionEnforcer permissionEnforcer,
- @Nullable AdvancedProtectionHook hook) {
+ @Nullable AdvancedProtectionHook hook, @Nullable AdvancedProtectionProvider provider) {
super(permissionEnforcer);
mContext = context;
mStore = store;
@@ -85,6 +94,10 @@ public class AdvancedProtectionService extends IAdvancedProtectionService.Stub
if (hook != null) {
mHooks.add(hook);
}
+
+ if (provider != null) {
+ mProviders.add(provider);
+ }
}
@Override
@@ -146,6 +159,25 @@ public class AdvancedProtectionService extends IAdvancedProtectionService.Stub
}
@Override
+ @EnforcePermission(Manifest.permission.SET_ADVANCED_PROTECTION_MODE)
+ public List<AdvancedProtectionFeature> getAdvancedProtectionFeatures() {
+ getAdvancedProtectionFeatures_enforcePermission();
+ List<AdvancedProtectionFeature> features = new ArrayList<>();
+ for (int i = 0; i < mProviders.size(); i++) {
+ features.addAll(mProviders.get(i).getFeatures());
+ }
+
+ for (int i = 0; i < mHooks.size(); i++) {
+ AdvancedProtectionHook hook = mHooks.get(i);
+ if (hook.isAvailable()) {
+ features.add(hook.getFeature());
+ }
+ }
+
+ return features;
+ }
+
+ @Override
public void onShellCommand(FileDescriptor in, FileDescriptor out,
FileDescriptor err, @NonNull String[] args, ShellCallback callback,
@NonNull ResultReceiver resultReceiver) {
diff --git a/services/core/java/com/android/server/security/advancedprotection/features/AdvancedProtectionHook.java b/services/core/java/com/android/server/security/advancedprotection/features/AdvancedProtectionHook.java
index b2acc511950b..f82db9676c2c 100644
--- a/services/core/java/com/android/server/security/advancedprotection/features/AdvancedProtectionHook.java
+++ b/services/core/java/com/android/server/security/advancedprotection/features/AdvancedProtectionHook.java
@@ -18,11 +18,15 @@ package com.android.server.security.advancedprotection.features;
import android.annotation.NonNull;
import android.content.Context;
+import android.security.advancedprotection.AdvancedProtectionFeature;
/** @hide */
public abstract class AdvancedProtectionHook {
/** Called on boot phase PHASE_SYSTEM_SERVICES_READY */
public AdvancedProtectionHook(@NonNull Context context, boolean enabled) {}
+ /** The feature this hook provides */
+ @NonNull
+ public abstract AdvancedProtectionFeature getFeature();
/** Whether this feature is relevant on this device. If false, onAdvancedProtectionChanged will
* not be called, and the feature will not be displayed in the onboarding UX. */
public abstract boolean isAvailable();
diff --git a/services/core/java/com/android/server/security/advancedprotection/features/AdvancedProtectionProvider.java b/services/core/java/com/android/server/security/advancedprotection/features/AdvancedProtectionProvider.java
new file mode 100644
index 000000000000..ed451f1e2257
--- /dev/null
+++ b/services/core/java/com/android/server/security/advancedprotection/features/AdvancedProtectionProvider.java
@@ -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.server.security.advancedprotection.features;
+
+import android.security.advancedprotection.AdvancedProtectionFeature;
+
+import java.util.List;
+
+/** @hide */
+public abstract class AdvancedProtectionProvider {
+ /** The list of features provided */
+ public abstract List<AdvancedProtectionFeature> getFeatures();
+}
diff --git a/services/core/java/com/android/server/stats/pull/netstats/NetworkStatsAccumulator.java b/services/core/java/com/android/server/stats/pull/netstats/NetworkStatsAccumulator.java
index e798bc487031..3f7fcee5bb9a 100644
--- a/services/core/java/com/android/server/stats/pull/netstats/NetworkStatsAccumulator.java
+++ b/services/core/java/com/android/server/stats/pull/netstats/NetworkStatsAccumulator.java
@@ -19,6 +19,7 @@ package com.android.server.stats.pull.netstats;
import android.annotation.NonNull;
import android.net.NetworkStats;
import android.net.NetworkTemplate;
+import android.util.Log;
import java.util.Objects;
@@ -33,6 +34,7 @@ import java.util.Objects;
*/
public class NetworkStatsAccumulator {
+ private static final String TAG = "NetworkStatsAccumulator";
private final NetworkTemplate mTemplate;
private final boolean mWithTags;
private final long mBucketDurationMillis;
@@ -57,8 +59,9 @@ public class NetworkStatsAccumulator {
@NonNull
public NetworkStats queryStats(long currentTimeMillis,
@NonNull StatsQueryFunction queryFunction) {
- maybeExpandSnapshot(currentTimeMillis, queryFunction);
- return snapshotPlusFollowingStats(currentTimeMillis, queryFunction);
+ NetworkStats completeStats = snapshotPlusFollowingStats(currentTimeMillis, queryFunction);
+ maybeExpandSnapshot(currentTimeMillis, completeStats, queryFunction);
+ return completeStats;
}
/**
@@ -72,15 +75,28 @@ public class NetworkStatsAccumulator {
* Expands the internal cumulative stats snapshot, if possible, by querying NetworkStats.
*/
private void maybeExpandSnapshot(long currentTimeMillis,
+ NetworkStats completeStatsUntilCurrentTime,
@NonNull StatsQueryFunction queryFunction) {
// Update snapshot only if it is possible to expand it by at least one full bucket, and only
// if the new snapshot's end is not in the active bucket.
long newEndTimeMillis = currentTimeMillis - mBucketDurationMillis;
if (newEndTimeMillis - mSnapshotEndTimeMillis > mBucketDurationMillis) {
- NetworkStats extraStats = queryFunction.queryNetworkStats(mTemplate, mWithTags,
- mSnapshotEndTimeMillis, newEndTimeMillis);
+ Log.v(TAG,
+ "Expanding snapshot (mTemplate=" + mTemplate + ", mWithTags=" + mWithTags
+ + ") from " + mSnapshotEndTimeMillis + " to " + newEndTimeMillis
+ + " at " + currentTimeMillis);
+ NetworkStats extraStats = queryFunction.queryNetworkStats(
+ mTemplate, mWithTags, mSnapshotEndTimeMillis, newEndTimeMillis);
mSnapshot = mSnapshot.add(extraStats);
mSnapshotEndTimeMillis = newEndTimeMillis;
+
+ // NetworkStats queries interpolate historical data using integers maths, which makes
+ // queries non-transitive: Query(t0, t1) + Query(t1, t2) <= Query(t0, t2).
+ // Compute interpolation data loss from moving the snapshot's end-point, and add it to
+ // the snapshot to avoid under-counting.
+ NetworkStats newStats = snapshotPlusFollowingStats(currentTimeMillis, queryFunction);
+ NetworkStats interpolationLoss = completeStatsUntilCurrentTime.subtract(newStats);
+ mSnapshot = mSnapshot.add(interpolationLoss);
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityRecordInputSink.java b/services/core/java/com/android/server/wm/ActivityRecordInputSink.java
index fa5beca31ec1..ea6506a03a80 100644
--- a/services/core/java/com/android/server/wm/ActivityRecordInputSink.java
+++ b/services/core/java/com/android/server/wm/ActivityRecordInputSink.java
@@ -40,7 +40,6 @@ class ActivityRecordInputSink {
@ChangeId
static final long ENABLE_TOUCH_OPAQUE_ACTIVITIES = 194480991L;
- // TODO(b/369605358) Update EnabledSince when SDK 36 version code is available.
/**
* If the app's target SDK is 36+, pass-through touches from a cross-uid overlaying activity is
* blocked by default. The activity may opt in to receive pass-through touches using
@@ -52,7 +51,7 @@ class ActivityRecordInputSink {
* @see ActivityOptions#setAllowPassThroughOnTouchOutside
*/
@ChangeId
- @EnabledSince(targetSdkVersion = Build.VERSION_CODES.CUR_DEVELOPMENT)
+ @EnabledSince(targetSdkVersion = Build.VERSION_CODES.BAKLAVA)
static final long ENABLE_OVERLAY_TOUCH_PASS_THROUGH_OPT_IN_ENFORCEMENT = 358129114L;
private final ActivityRecord mActivityRecord;
diff --git a/services/core/java/com/android/server/wm/ActivityStartController.java b/services/core/java/com/android/server/wm/ActivityStartController.java
index 6e97f8a943dd..3f24da9d89f2 100644
--- a/services/core/java/com/android/server/wm/ActivityStartController.java
+++ b/services/core/java/com/android/server/wm/ActivityStartController.java
@@ -478,7 +478,7 @@ public class ActivityStartController {
intentGrants.merge(creatorIntentGrants);
}
} catch (SecurityException securityException) {
- ActivityStarter.logAndThrowExceptionForIntentRedirect(
+ ActivityStarter.logAndThrowExceptionForIntentRedirect(mService.mContext,
"Creator URI Grant Caused Exception.", intent, creatorUid,
creatorPackage, filterCallingUid, callingPackage,
securityException);
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index e15968db9b3d..05a96d9fcf5e 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -55,6 +55,7 @@ import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TOP;
import static android.content.pm.ActivityInfo.launchModeToString;
import static android.os.Process.INVALID_UID;
import static android.security.Flags.preventIntentRedirectAbortOrThrowException;
+import static android.security.Flags.preventIntentRedirectShowToast;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowManager.TRANSIT_NONE;
import static android.view.WindowManager.TRANSIT_OPEN;
@@ -105,6 +106,7 @@ import android.compat.annotation.ChangeId;
import android.compat.annotation.Disabled;
import android.compat.annotation.EnabledSince;
import android.compat.annotation.Overridable;
+import android.content.Context;
import android.content.IIntentSender;
import android.content.Intent;
import android.content.IntentSender;
@@ -128,12 +130,14 @@ import android.service.voice.IVoiceInteractionSession;
import android.text.TextUtils;
import android.util.Pools.SynchronizedPool;
import android.util.Slog;
+import android.widget.Toast;
import android.window.RemoteTransition;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.HeavyWeightSwitcherActivity;
import com.android.internal.app.IVoiceInteractor;
import com.android.internal.protolog.ProtoLog;
+import com.android.server.UiThread;
import com.android.server.am.ActivityManagerService.IntentCreatorToken;
import com.android.server.am.PendingIntentRecord;
import com.android.server.pm.InstantAppResolver;
@@ -614,7 +618,7 @@ class ActivityStarter {
// Check if the Intent was redirected
if ((intent.getExtendedFlags() & Intent.EXTENDED_FLAG_MISSING_CREATOR_OR_INVALID_TOKEN)
!= 0) {
- ActivityStarter.logAndThrowExceptionForIntentRedirect(
+ logAndThrowExceptionForIntentRedirect(supervisor.mService.mContext,
"Unparceled intent does not have a creator token set.", intent,
intentCreatorUid, intentCreatorPackage, resolvedCallingUid,
resolvedCallingPackage, null);
@@ -650,7 +654,7 @@ class ActivityStarter {
intentGrants.merge(creatorIntentGrants);
}
} catch (SecurityException securityException) {
- ActivityStarter.logAndThrowExceptionForIntentRedirect(
+ logAndThrowExceptionForIntentRedirect(supervisor.mService.mContext,
"Creator URI Grant Caused Exception.", intent, intentCreatorUid,
intentCreatorPackage, resolvedCallingUid,
resolvedCallingPackage, securityException);
@@ -674,7 +678,7 @@ class ActivityStarter {
intentGrants.merge(creatorIntentGrants);
}
} catch (SecurityException securityException) {
- ActivityStarter.logAndThrowExceptionForIntentRedirect(
+ logAndThrowExceptionForIntentRedirect(supervisor.mService.mContext,
"Creator URI Grant Caused Exception.", intent, intentCreatorUid,
intentCreatorPackage, resolvedCallingUid,
resolvedCallingPackage, securityException);
@@ -1250,27 +1254,27 @@ class ActivityStarter {
requestCode, 0, intentCreatorUid, intentCreatorPackage, "",
request.ignoreTargetSecurity, inTask != null, null, resultRecord,
resultRootTask)) {
- abort = logAndAbortForIntentRedirect(
+ abort = logAndAbortForIntentRedirect(mService.mContext,
"Creator checkStartAnyActivityPermission Caused abortion.",
intent, intentCreatorUid, intentCreatorPackage, callingUid,
callingPackage);
}
} catch (SecurityException e) {
- logAndThrowExceptionForIntentRedirect(
+ logAndThrowExceptionForIntentRedirect(mService.mContext,
"Creator checkStartAnyActivityPermission Caused Exception.",
intent, intentCreatorUid, intentCreatorPackage, callingUid, callingPackage,
e);
}
if (!mService.mIntentFirewall.checkStartActivity(intent, intentCreatorUid,
0, resolvedType, aInfo.applicationInfo)) {
- abort = logAndAbortForIntentRedirect(
+ abort = logAndAbortForIntentRedirect(mService.mContext,
"Creator IntentFirewall.checkStartActivity Caused abortion.",
intent, intentCreatorUid, intentCreatorPackage, callingUid, callingPackage);
}
if (!mService.getPermissionPolicyInternal().checkStartActivity(intent,
intentCreatorUid, intentCreatorPackage)) {
- abort = logAndAbortForIntentRedirect(
+ abort = logAndAbortForIntentRedirect(mService.mContext,
"Creator PermissionPolicyService.checkStartActivity Caused abortion.",
intent, intentCreatorUid, intentCreatorPackage, callingUid, callingPackage);
}
@@ -3596,25 +3600,38 @@ class ActivityStarter {
pw.println(mInTaskFragment);
}
- static void logAndThrowExceptionForIntentRedirect(@NonNull String message,
- @NonNull Intent intent, int intentCreatorUid, @Nullable String intentCreatorPackage,
- int callingUid, @Nullable String callingPackage,
+ static void logAndThrowExceptionForIntentRedirect(@NonNull Context context,
+ @NonNull String message, @NonNull Intent intent, int intentCreatorUid,
+ @Nullable String intentCreatorPackage, int callingUid, @Nullable String callingPackage,
@Nullable SecurityException originalException) {
String msg = getIntentRedirectPreventedLogMessage(message, intent, intentCreatorUid,
intentCreatorPackage, callingUid, callingPackage);
Slog.wtf(TAG, msg);
+ if (preventIntentRedirectShowToast()) {
+ UiThread.getHandler().post(
+ () -> Toast.makeText(context,
+ "Activity launch blocked. go/report-bug-intentRedir to report a bug",
+ Toast.LENGTH_LONG).show());
+ }
if (preventIntentRedirectAbortOrThrowException() && CompatChanges.isChangeEnabled(
ENABLE_PREVENT_INTENT_REDIRECT_TAKE_ACTION, callingUid)) {
throw new SecurityException(msg, originalException);
}
}
- private static boolean logAndAbortForIntentRedirect(@NonNull String message,
- @NonNull Intent intent, int intentCreatorUid, @Nullable String intentCreatorPackage,
- int callingUid, @Nullable String callingPackage) {
+ private static boolean logAndAbortForIntentRedirect(@NonNull Context context,
+ @NonNull String message, @NonNull Intent intent, int intentCreatorUid,
+ @Nullable String intentCreatorPackage, int callingUid,
+ @Nullable String callingPackage) {
String msg = getIntentRedirectPreventedLogMessage(message, intent, intentCreatorUid,
intentCreatorPackage, callingUid, callingPackage);
Slog.wtf(TAG, msg);
+ if (preventIntentRedirectShowToast()) {
+ UiThread.getHandler().post(
+ () -> Toast.makeText(context,
+ "Activity launch blocked. go/report-bug-intentRedir to report a bug",
+ Toast.LENGTH_LONG).show());
+ }
return preventIntentRedirectAbortOrThrowException() && CompatChanges.isChangeEnabled(
ENABLE_PREVENT_INTENT_REDIRECT_TAKE_ACTION, callingUid);
}
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index b036cfc9da2b..0745fecdcd76 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -5896,6 +5896,29 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
}
/**
+ * Registers an app that uses the Strict Mode for detecting BAL.
+ *
+ * @param callback the callback to register
+ * @return {@code true} if the callback was registered successfully.
+ */
+ @Override
+ public boolean registerBackgroundActivityStartCallback(IBinder callback) {
+ return mTaskSupervisor.getBackgroundActivityLaunchController()
+ .addStrictModeCallback(Binder.getCallingUid(), callback);
+ }
+
+ /**
+ * Unregisters an app that uses the Strict Mode for detecting BAL.
+ *
+ * @param callback the callback to unregister
+ */
+ @Override
+ public void unregisterBackgroundActivityStartCallback(IBinder callback) {
+ mTaskSupervisor.getBackgroundActivityLaunchController()
+ .removeStrictModeCallback(Binder.getCallingUid(), callback);
+ }
+
+ /**
* Wrap the {@link ActivityOptions} in {@link SafeActivityOptions} and attach caller options
* that allow using the callers permissions to start background activities.
*/
diff --git a/services/core/java/com/android/server/wm/AppCompatAspectRatioOverrides.java b/services/core/java/com/android/server/wm/AppCompatAspectRatioOverrides.java
index f1dd41e7d74a..90c0866d7dff 100644
--- a/services/core/java/com/android/server/wm/AppCompatAspectRatioOverrides.java
+++ b/services/core/java/com/android/server/wm/AppCompatAspectRatioOverrides.java
@@ -37,6 +37,7 @@ import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLAS
import static com.android.server.wm.AppCompatConfiguration.LETTERBOX_POSITION_MULTIPLIER_CENTER;
import static com.android.server.wm.AppCompatConfiguration.MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO;
import static com.android.server.wm.AppCompatUtils.isChangeEnabled;
+import static com.android.server.wm.AppCompatUtils.isDisplayIgnoreActivitySizeRestrictions;
import android.annotation.NonNull;
import android.content.pm.IPackageManager;
@@ -175,8 +176,17 @@ class AppCompatAspectRatioOverrides {
&& mActivityRecord.mDisplayContent.getIgnoreOrientationRequest();
}
+ /**
+ * Whether to ignore fixed orientation, aspect ratio and resizability of activity.
+ */
boolean hasFullscreenOverride() {
- return shouldApplyUserFullscreenOverride() || isSystemOverrideToFullscreenEnabled();
+ return shouldApplyUserFullscreenOverride() || isSystemOverrideToFullscreenEnabled()
+ || shouldIgnoreActivitySizeRestrictionsForDisplay();
+ }
+
+ boolean shouldIgnoreActivitySizeRestrictionsForDisplay() {
+ return isDisplayIgnoreActivitySizeRestrictions(mActivityRecord)
+ && !mAllowOrientationOverrideOptProp.isFalse();
}
float getUserMinAspectRatio() {
diff --git a/services/core/java/com/android/server/wm/AppCompatOrientationPolicy.java b/services/core/java/com/android/server/wm/AppCompatOrientationPolicy.java
index e3a9d672c17f..7aed33d94223 100644
--- a/services/core/java/com/android/server/wm/AppCompatOrientationPolicy.java
+++ b/services/core/java/com/android/server/wm/AppCompatOrientationPolicy.java
@@ -53,11 +53,16 @@ class AppCompatOrientationPolicy {
@ActivityInfo.ScreenOrientation
int overrideOrientationIfNeeded(@ActivityInfo.ScreenOrientation int candidate) {
+ final AppCompatAspectRatioOverrides aspectRatioOverrides =
+ mAppCompatOverrides.getAppCompatAspectRatioOverrides();
+ // Ignore all orientation requests of activities for eligible virtual displays.
+ if (aspectRatioOverrides.shouldIgnoreActivitySizeRestrictionsForDisplay()) {
+ return SCREEN_ORIENTATION_USER;
+ }
final DisplayContent displayContent = mActivityRecord.mDisplayContent;
final boolean isIgnoreOrientationRequestEnabled = displayContent != null
&& displayContent.getIgnoreOrientationRequest();
- final boolean hasFullscreenOverride = mAppCompatOverrides
- .getAppCompatAspectRatioOverrides().hasFullscreenOverride();
+ final boolean hasFullscreenOverride = aspectRatioOverrides.hasFullscreenOverride();
final boolean shouldCameraCompatControlOrientation =
AppCompatCameraPolicy.shouldCameraCompatControlOrientation(mActivityRecord);
if (hasFullscreenOverride && isIgnoreOrientationRequestEnabled
@@ -76,8 +81,8 @@ class AppCompatOrientationPolicy {
// In some cases (e.g. Kids app) we need to map the candidate orientation to some other
// orientation.
candidate = mActivityRecord.mWmService.mapOrientationRequest(candidate);
- final boolean shouldApplyUserMinAspectRatioOverride = mAppCompatOverrides
- .getAppCompatAspectRatioOverrides().shouldApplyUserMinAspectRatioOverride();
+ final boolean shouldApplyUserMinAspectRatioOverride = aspectRatioOverrides
+ .shouldApplyUserMinAspectRatioOverride();
if (shouldApplyUserMinAspectRatioOverride && (!isFixedOrientation(candidate)
|| candidate == SCREEN_ORIENTATION_LOCKED)) {
Slog.v(TAG, "Requested orientation " + screenOrientationToString(candidate)
diff --git a/services/core/java/com/android/server/wm/AppCompatSizeCompatModePolicy.java b/services/core/java/com/android/server/wm/AppCompatSizeCompatModePolicy.java
index f069dcdbc86b..d0d3d4321a0a 100644
--- a/services/core/java/com/android/server/wm/AppCompatSizeCompatModePolicy.java
+++ b/services/core/java/com/android/server/wm/AppCompatSizeCompatModePolicy.java
@@ -149,7 +149,7 @@ class AppCompatSizeCompatModePolicy {
@NonNull Configuration newParentConfig) {
mSizeCompatScale = mActivityRecord.mAppCompatController.getTransparentPolicy()
.findOpaqueNotFinishingActivityBelow()
- .map(activityRecord -> mSizeCompatScale)
+ .map(ar -> Math.min(1.0f, ar.getCompatScale()))
.orElseGet(() -> calculateSizeCompatScale(
resolvedAppBounds, containerAppBounds, newParentConfig));
}
diff --git a/services/core/java/com/android/server/wm/AppCompatUtils.java b/services/core/java/com/android/server/wm/AppCompatUtils.java
index 8d8424820990..db76eb9ac5d9 100644
--- a/services/core/java/com/android/server/wm/AppCompatUtils.java
+++ b/services/core/java/com/android/server/wm/AppCompatUtils.java
@@ -32,6 +32,8 @@ import android.view.InsetsSource;
import android.view.InsetsState;
import android.view.WindowInsets;
+import com.android.window.flags.Flags;
+
import java.util.function.BooleanSupplier;
/**
@@ -86,10 +88,22 @@ final class AppCompatUtils {
/**
* @param activityRecord The {@link ActivityRecord} for the app package.
* @param overrideChangeId The per-app override identifier.
- * @return {@code true} if the per-app override is enable for the given activity.
+ * @return {@code true} if the per-app override is enable for the given activity and the
+ * display does not ignore fixed orientation, aspect ratio and resizability of activity.
*/
static boolean isChangeEnabled(@NonNull ActivityRecord activityRecord, long overrideChangeId) {
- return activityRecord.info.isChangeEnabled(overrideChangeId);
+ return activityRecord.info.isChangeEnabled(overrideChangeId)
+ && !isDisplayIgnoreActivitySizeRestrictions(activityRecord);
+ }
+
+ /**
+ * Whether the display ignores fixed orientation, aspect ratio and resizability of activities.
+ */
+ static boolean isDisplayIgnoreActivitySizeRestrictions(
+ @NonNull ActivityRecord activityRecord) {
+ final DisplayContent dc = activityRecord.mDisplayContent;
+ return Flags.vdmForceAppUniversalResizableApi() && dc != null
+ && dc.isDisplayIgnoreActivitySizeRestrictions();
}
/**
diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java
index 5c53c2aeba4b..2fe023e55d31 100644
--- a/services/core/java/com/android/server/wm/BackNavigationController.java
+++ b/services/core/java/com/android/server/wm/BackNavigationController.java
@@ -107,6 +107,12 @@ class BackNavigationController {
mNavigationMonitor.onFocusWindowChanged(newFocus);
}
+ void onEmbeddedWindowGestureTransferred(@NonNull WindowState host) {
+ if (Flags.disallowAppProgressEmbeddedWindow()) {
+ mNavigationMonitor.onEmbeddedWindowGestureTransferred(host);
+ }
+ }
+
/**
* Set up the necessary leashes and build a {@link BackNavigationInfo} instance for an upcoming
* back gesture animation.
@@ -178,6 +184,9 @@ class BackNavigationController {
return null;
}
+ final ArrayList<EmbeddedWindowController.EmbeddedWindow> embeddedWindows = wmService
+ .mEmbeddedWindowController.getByHostWindow(window);
+
currentActivity = window.mActivityRecord;
currentTask = window.getTask();
if ((currentTask != null && !currentTask.isVisibleRequested())
@@ -199,11 +208,22 @@ class BackNavigationController {
infoBuilder.setOnBackInvokedCallback(callbackInfo.getCallback());
infoBuilder.setAnimationCallback(callbackInfo.isAnimationCallback());
infoBuilder.setTouchableRegion(window.getFrame());
- infoBuilder.setAppProgressAllowed((window.getAttrs().privateFlags
- & PRIVATE_FLAG_APP_PROGRESS_GENERATION_ALLOWED) != 0);
if (currentTask != null) {
infoBuilder.setFocusedTaskId(currentTask.mTaskId);
}
+ boolean transferGestureToEmbedded = false;
+ if (Flags.disallowAppProgressEmbeddedWindow() && embeddedWindows != null) {
+ for (int i = embeddedWindows.size() - 1; i >= 0; --i) {
+ if (embeddedWindows.get(i).mGestureToEmbedded) {
+ transferGestureToEmbedded = true;
+ break;
+ }
+ }
+ }
+ final boolean canInterruptInView = (window.getAttrs().privateFlags
+ & PRIVATE_FLAG_APP_PROGRESS_GENERATION_ALLOWED) != 0;
+ infoBuilder.setAppProgressAllowed(canInterruptInView && !transferGestureToEmbedded
+ && callbackInfo.isAnimationCallback());
mNavigationMonitor.startMonitor(window, navigationObserver);
ProtoLog.d(WM_DEBUG_BACK_PREVIEW, "startBackNavigation currentTask=%s, "
@@ -742,6 +762,20 @@ class BackNavigationController {
}
/**
+ * Notify focus window has transferred touch gesture to embedded window. Shell should pilfer
+ * pointers so embedded process won't receive motion event.
+ *
+ */
+ void onEmbeddedWindowGestureTransferred(@NonNull WindowState host) {
+ if (!isMonitorForRemote() || host != mNavigatingWindow) {
+ return;
+ }
+ final Bundle result = new Bundle();
+ result.putBoolean(BackNavigationInfo.KEY_TOUCH_GESTURE_TRANSFERRED, true);
+ mObserver.sendResult(result);
+ }
+
+ /**
* Notify an unexpected transition has happened during back navigation.
*/
private void onTransitionReadyWhileNavigate(ArrayList<WindowContainer> opening,
diff --git a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
index 9c7a6f910948..1933408afae5 100644
--- a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
+++ b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
@@ -52,6 +52,7 @@ import static com.android.window.flags.Flags.balRequireOptInByPendingIntentCreat
import static com.android.window.flags.Flags.balRequireOptInSameUid;
import static com.android.window.flags.Flags.balRespectAppSwitchStateWhenCheckBoundByForegroundUid;
import static com.android.window.flags.Flags.balShowToastsBlocked;
+import static com.android.window.flags.Flags.balStrictModeRo;
import static java.lang.annotation.RetentionPolicy.SOURCE;
import static java.util.Objects.requireNonNull;
@@ -63,6 +64,7 @@ import android.app.ActivityManager;
import android.app.ActivityOptions;
import android.app.AppOpsManager;
import android.app.BackgroundStartPrivileges;
+import android.app.IBackgroundActivityLaunchCallback;
import android.app.compat.CompatChanges;
import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledAfter;
@@ -70,13 +72,17 @@ import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
+import android.os.IBinder;
import android.os.Process;
+import android.os.RemoteException;
import android.os.SystemClock;
import android.os.UserHandle;
import android.provider.DeviceConfig;
+import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.DebugUtils;
import android.util.Slog;
+import android.util.SparseArray;
import android.widget.Toast;
import com.android.internal.R;
@@ -91,6 +97,7 @@ import com.android.server.wm.BackgroundLaunchProcessController.BalCheckConfigura
import java.lang.annotation.Retention;
import java.util.ArrayList;
import java.util.HashMap;
+import java.util.Map;
import java.util.StringJoiner;
import java.util.function.Consumer;
import java.util.function.Function;
@@ -141,6 +148,10 @@ public class BackgroundActivityStartController {
private final ActivityTaskManagerService mService;
private final ActivityTaskSupervisor mSupervisor;
+ @GuardedBy("mStrictModeBalCallbacks")
+ private final SparseArray<ArrayMap<IBinder, IBackgroundActivityLaunchCallback>>
+ mStrictModeBalCallbacks = new SparseArray<>();
+
// TODO(b/263368846) Rename when ASM logic is moved in
@Retention(SOURCE)
@@ -839,7 +850,120 @@ public class BackgroundActivityStartController {
// only show a toast if either caller or real caller could launch if they opted in
showToast("BAL blocked. goo.gle/android-bal");
}
- return statsLog(BalVerdict.BLOCK, state);
+ BalVerdict verdict = statsLog(BalVerdict.BLOCK, state);
+ if (balStrictModeRo()) {
+ String abortDebugMessage;
+ if (state.isPendingIntent()) {
+ abortDebugMessage =
+ "PendingIntent Activity start blocked in " + state.mRealCallingPackage
+ + ". "
+ + "PendingIntent was created in " + state.mCallingPackage
+ + ". "
+ + (state.mResultForRealCaller.allows()
+ ? state.mRealCallingPackage
+ + " could opt in to grant BAL privileges when sending. "
+ : "")
+ + (state.mResultForCaller.allows()
+ ? state.mCallingPackage
+ + " could opt in to grant BAL privileges when creating."
+ : "")
+ + "The intent would have started " + state.mIntent.getComponent();
+ } else {
+ abortDebugMessage = "Activity start blocked. "
+ + "The intent would have started " + state.mIntent.getComponent();
+ }
+ strictModeLaunchAborted(state.mCallingUid, abortDebugMessage);
+ if (!state.callerIsRealCaller()) {
+ strictModeLaunchAborted(state.mRealCallingUid, abortDebugMessage);
+ }
+ }
+ return verdict;
+ }
+
+ /**
+ * Retrieve a registered strict mode callback for BAL.
+ * @param uid the uid of the app.
+ * @return the callback if it exists, returns <code>null</code> otherwise.
+ */
+ @Nullable
+ Map<IBinder, IBackgroundActivityLaunchCallback> getStrictModeBalCallbacks(int uid) {
+ ArrayMap<IBinder, IBackgroundActivityLaunchCallback> callbackMap;
+ synchronized (mStrictModeBalCallbacks) {
+ callbackMap =
+ mStrictModeBalCallbacks.get(uid);
+ if (callbackMap == null) {
+ return null;
+ }
+ return new ArrayMap<>(callbackMap);
+ }
+ }
+
+ /**
+ * Add strict mode callback for BAL.
+ *
+ * @param uid the UID for which the binder is registered.
+ * @param callback the {@link IBackgroundActivityLaunchCallback} binder to call when BAL is
+ * blocked.
+ * @return {@code true} if the callback has been successfully added.
+ */
+ boolean addStrictModeCallback(int uid, IBinder callback) {
+ IBackgroundActivityLaunchCallback balCallback =
+ IBackgroundActivityLaunchCallback.Stub.asInterface(callback);
+ synchronized (mStrictModeBalCallbacks) {
+ ArrayMap<IBinder, IBackgroundActivityLaunchCallback> callbackMap =
+ mStrictModeBalCallbacks.get(uid);
+ if (callbackMap == null) {
+ callbackMap = new ArrayMap<>();
+ mStrictModeBalCallbacks.put(uid, callbackMap);
+ }
+ if (callbackMap.containsKey(callback)) {
+ return false;
+ }
+ callbackMap.put(callback, balCallback);
+ }
+ try {
+ callback.linkToDeath(() -> removeStrictModeCallback(uid, callback), 0);
+ } catch (RemoteException e) {
+ removeStrictModeCallback(uid, callback);
+ }
+ return true;
+ }
+
+ /**
+ * Remove strict mode callback for BAL.
+ *
+ * @param uid the UID for which the binder is registered.
+ * @param callback the {@link IBackgroundActivityLaunchCallback} binder to call when BAL is
+ * blocked.
+ */
+ void removeStrictModeCallback(int uid, IBinder callback) {
+ synchronized (mStrictModeBalCallbacks) {
+ Map<IBinder, IBackgroundActivityLaunchCallback> callbackMap =
+ mStrictModeBalCallbacks.get(uid);
+ if (callback == null || !callbackMap.containsKey(callback)) {
+ return;
+ }
+ callbackMap.remove(callback);
+ if (callbackMap.isEmpty()) {
+ mStrictModeBalCallbacks.remove(uid);
+ }
+ }
+ }
+
+ private void strictModeLaunchAborted(int callingUid, String message) {
+ Map<IBinder, IBackgroundActivityLaunchCallback> strictModeBalCallbacks =
+ getStrictModeBalCallbacks(callingUid);
+ if (strictModeBalCallbacks == null) {
+ return;
+ }
+ for (Map.Entry<IBinder, IBackgroundActivityLaunchCallback> callbackEntry :
+ strictModeBalCallbacks.entrySet()) {
+ try {
+ callbackEntry.getValue().onBackgroundActivityLaunchAborted(message);
+ } catch (RemoteException e) {
+ removeStrictModeCallback(callingUid, callbackEntry.getKey());
+ }
+ }
}
/**
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index aac756f4e559..e827f44cb2a2 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -5789,6 +5789,17 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
}
/**
+ * Checks if this display is allowed to ignore fixed orientation, aspect ratio,
+ * and resizability of apps.
+ *
+ * <p>This can be set via
+ * {@link VirtualDisplayConfig.Builder#setIgnoreActivitySizeRestrictions}.</p>
+ */
+ boolean isDisplayIgnoreActivitySizeRestrictions() {
+ return mWmService.mDisplayWindowSettings.isIgnoreActivitySizeRestrictionsLocked(this);
+ }
+
+ /**
* The direct child layer of the display to put all non-overlay windows. This is also used for
* screen rotation animation so that there is a parent layer to put the animation leash.
*/
diff --git a/services/core/java/com/android/server/wm/DisplayWindowSettings.java b/services/core/java/com/android/server/wm/DisplayWindowSettings.java
index e585efa8a3cc..c87b811b4231 100644
--- a/services/core/java/com/android/server/wm/DisplayWindowSettings.java
+++ b/services/core/java/com/android/server/wm/DisplayWindowSettings.java
@@ -275,6 +275,24 @@ class DisplayWindowSettings {
mSettingsProvider.updateOverrideSettings(displayInfo, overrideSettings);
}
+ boolean isIgnoreActivitySizeRestrictionsLocked(@NonNull DisplayContent dc) {
+ final DisplayInfo displayInfo = dc.getDisplayInfo();
+ final SettingsProvider.SettingsEntry settings = mSettingsProvider.getSettings(displayInfo);
+ return settings.mIgnoreActivitySizeRestrictions != null
+ && settings.mIgnoreActivitySizeRestrictions;
+ }
+
+ void setIgnoreActivitySizeRestrictionsOnDisplayLocked(@NonNull String displayUniqueId,
+ int displayType, boolean enabled) {
+ final DisplayInfo displayInfo = new DisplayInfo();
+ displayInfo.uniqueId = displayUniqueId;
+ displayInfo.type = displayType;
+ final SettingsProvider.SettingsEntry overrideSettings =
+ mSettingsProvider.getOverrideSettings(displayInfo);
+ overrideSettings.mIgnoreActivitySizeRestrictions = enabled;
+ mSettingsProvider.updateOverrideSettings(displayInfo, overrideSettings);
+ }
+
void clearDisplaySettings(@NonNull String displayUniqueId, int displayType) {
final DisplayInfo displayInfo = new DisplayInfo();
displayInfo.uniqueId = displayUniqueId;
@@ -474,6 +492,8 @@ class DisplayWindowSettings {
Boolean mIgnoreDisplayCutout;
@Nullable
Boolean mDontMoveToTop;
+ @Nullable
+ Boolean mIgnoreActivitySizeRestrictions;
SettingsEntry() {}
@@ -557,6 +577,11 @@ class DisplayWindowSettings {
mDontMoveToTop = other.mDontMoveToTop;
changed = true;
}
+ if (!Objects.equals(other.mIgnoreActivitySizeRestrictions,
+ mIgnoreActivitySizeRestrictions)) {
+ mIgnoreActivitySizeRestrictions = other.mIgnoreActivitySizeRestrictions;
+ changed = true;
+ }
return changed;
}
@@ -649,6 +674,11 @@ class DisplayWindowSettings {
mDontMoveToTop = delta.mDontMoveToTop;
changed = true;
}
+ if (delta.mIgnoreActivitySizeRestrictions != null && !Objects.equals(
+ delta.mIgnoreActivitySizeRestrictions, mIgnoreActivitySizeRestrictions)) {
+ mIgnoreActivitySizeRestrictions = delta.mIgnoreActivitySizeRestrictions;
+ changed = true;
+ }
return changed;
}
@@ -667,7 +697,8 @@ class DisplayWindowSettings {
&& mFixedToUserRotation == null
&& mIgnoreOrientationRequest == null
&& mIgnoreDisplayCutout == null
- && mDontMoveToTop == null;
+ && mDontMoveToTop == null
+ && mIgnoreActivitySizeRestrictions == null;
}
@Override
@@ -691,7 +722,9 @@ class DisplayWindowSettings {
&& Objects.equals(mFixedToUserRotation, that.mFixedToUserRotation)
&& Objects.equals(mIgnoreOrientationRequest, that.mIgnoreOrientationRequest)
&& Objects.equals(mIgnoreDisplayCutout, that.mIgnoreDisplayCutout)
- && Objects.equals(mDontMoveToTop, that.mDontMoveToTop);
+ && Objects.equals(mDontMoveToTop, that.mDontMoveToTop)
+ && Objects.equals(mIgnoreActivitySizeRestrictions,
+ that.mIgnoreActivitySizeRestrictions);
}
@Override
@@ -700,7 +733,7 @@ class DisplayWindowSettings {
mForcedHeight, mForcedDensity, mForcedScalingMode, mRemoveContentMode,
mShouldShowWithInsecureKeyguard, mShouldShowSystemDecors, mIsHomeSupported,
mImePolicy, mFixedToUserRotation, mIgnoreOrientationRequest,
- mIgnoreDisplayCutout, mDontMoveToTop);
+ mIgnoreDisplayCutout, mDontMoveToTop, mIgnoreActivitySizeRestrictions);
}
@Override
@@ -722,6 +755,7 @@ class DisplayWindowSettings {
+ ", mIgnoreOrientationRequest=" + mIgnoreOrientationRequest
+ ", mIgnoreDisplayCutout=" + mIgnoreDisplayCutout
+ ", mDontMoveToTop=" + mDontMoveToTop
+ + ", mForceAppsUniversalResizable=" + mIgnoreActivitySizeRestrictions
+ '}';
}
}
diff --git a/services/core/java/com/android/server/wm/EmbeddedWindowController.java b/services/core/java/com/android/server/wm/EmbeddedWindowController.java
index 5ac4cf8b6483..907d0dc2e183 100644
--- a/services/core/java/com/android/server/wm/EmbeddedWindowController.java
+++ b/services/core/java/com/android/server/wm/EmbeddedWindowController.java
@@ -39,6 +39,8 @@ import android.window.InputTransferToken;
import com.android.internal.protolog.ProtoLog;
import com.android.server.input.InputManagerService;
+import java.util.ArrayList;
+
/**
* Keeps track of embedded windows.
*
@@ -146,6 +148,20 @@ class EmbeddedWindowController {
return mWindowsByWindowToken.get(windowToken);
}
+ @Nullable ArrayList<EmbeddedWindow> getByHostWindow(WindowState host) {
+ ArrayList<EmbeddedWindow> windows = null;
+ for (int i = mWindows.size() - 1; i >= 0; i--) {
+ final EmbeddedWindow ew = mWindows.valueAt(i);
+ if (ew.mHostWindowState == host) {
+ if (windows == null) {
+ windows = new ArrayList<>();
+ }
+ windows.add(ew);
+ }
+ }
+ return windows;
+ }
+
private boolean isValidTouchGestureParams(WindowState hostWindowState,
EmbeddedWindow embeddedWindow) {
if (embeddedWindow == null) {
@@ -191,8 +207,12 @@ class EmbeddedWindowController {
throw new SecurityException(
"Transfer request must originate from owner of transferFromToken");
}
- return mInputManagerService.transferTouchGesture(ew.getInputChannelToken(),
- transferToHostWindowState.mInputChannelToken);
+ final boolean didTransfer = mInputManagerService.transferTouchGesture(
+ ew.getInputChannelToken(), transferToHostWindowState.mInputChannelToken);
+ if (didTransfer) {
+ ew.mGestureToEmbedded = false;
+ }
+ return didTransfer;
}
boolean transferToEmbedded(int callingUid, WindowState hostWindowState,
@@ -205,8 +225,15 @@ class EmbeddedWindowController {
throw new SecurityException(
"Transfer request must originate from owner of transferFromToken");
}
- return mInputManagerService.transferTouchGesture(hostWindowState.mInputChannelToken,
+ final boolean didTransfer = mInputManagerService.transferTouchGesture(
+ hostWindowState.mInputChannelToken,
ew.getInputChannelToken());
+ if (didTransfer) {
+ ew.mGestureToEmbedded = true;
+ mAtmService.mBackNavigationController.onEmbeddedWindowGestureTransferred(
+ hostWindowState);
+ }
+ return didTransfer;
}
static class EmbeddedWindow implements InputTarget {
@@ -235,6 +262,9 @@ class EmbeddedWindowController {
// the host window.
private @WindowInsets.Type.InsetsType int mRequestedVisibleTypes = 0;
+ /** Whether the gesture is transferred to embedded window. */
+ boolean mGestureToEmbedded = false;
+
/**
* @param session calling session to check ownership of the window
* @param clientToken client token used to clean up the map if the embedding process dies
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index b14dd3f7f17c..8aa0530d3bc1 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -3464,7 +3464,8 @@ class Task extends TaskFragment {
return null;
}
final Rect windowFrame = mainWindow.getFrame();
- if (top.getBounds().equals(windowFrame)) {
+ final Rect parentFrame = mainWindow.getParentFrame();
+ if (parentFrame.equals(windowFrame)) {
return null;
}
return windowFrame;
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 6c92ae6bb3e7..552584222711 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -1117,7 +1117,9 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
*/
void onDisplayChanged(DisplayContent dc) {
if (mDisplayContent != null && mDisplayContent != dc) {
- mTransitionController.collect(this);
+ if (asWindowState() == null) {
+ mTransitionController.collect(this);
+ }
// Cancel any change transition queued-up for this container on the old display when
// this container is moved from the old display.
mDisplayContent.mClosingChangingContainers.remove(this);
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index 82d39a39d188..ce032b4f7f9a 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -859,6 +859,18 @@ public abstract class WindowManagerInternal {
public abstract boolean isHomeSupportedOnDisplay(int displayId);
/**
+ * Sets whether the relevant display content ignores fixed orientation, aspect ratio
+ * and resizability of apps.
+ *
+ * @param displayUniqueId The unique ID of the display. Note that the display may not yet be
+ * created, but whenever it is, this property will be applied.
+ * @param displayType The type of the display, e.g. {@link Display#TYPE_VIRTUAL}.
+ * @param enabled Whether app is universal resizable on this display.
+ */
+ public abstract void setIgnoreActivitySizeRestrictionsOnDisplay(
+ @NonNull String displayUniqueId, int displayType, boolean enabled);
+
+ /**
* Removes any settings relevant to the given display.
*
* <p>This may be used when a property is set for a display unique ID before the display
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 97bf587cd10f..88b2d229e083 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -8384,6 +8384,20 @@ public class WindowManagerService extends IWindowManager.Stub
}
@Override
+ public void setIgnoreActivitySizeRestrictionsOnDisplay(@NonNull String displayUniqueId,
+ int displayType, boolean enabled) {
+ final long origId = Binder.clearCallingIdentity();
+ try {
+ synchronized (mGlobalLock) {
+ mDisplayWindowSettings.setIgnoreActivitySizeRestrictionsOnDisplayLocked(
+ displayUniqueId, displayType, enabled);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
+ }
+
+ @Override
public void clearDisplaySettings(String displayUniqueId, int displayType) {
final long origId = Binder.clearCallingIdentity();
try {
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 585e61795e7d..d01e29b2fd5e 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -258,6 +258,7 @@ import com.android.server.policy.WindowManagerPolicy;
import com.android.server.wm.LocalAnimationAdapter.AnimationSpec;
import com.android.server.wm.RefreshRatePolicy.FrameRateVote;
import com.android.server.wm.SurfaceAnimator.AnimationType;
+import com.android.server.wm.utils.RegionUtils;
import com.android.window.flags.Flags;
import dalvik.annotation.optimization.NeverCompile;
@@ -2327,7 +2328,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
if (mStartingData != null && mStartingData.mAssociatedTask == null
&& mTempConfiguration.windowConfiguration.getRotation()
== selfConfiguration.windowConfiguration.getRotation()
- && !mTempConfiguration.windowConfiguration.getBounds().equals(getBounds())) {
+ && !RegionUtils.sizeEquals(
+ mTempConfiguration.windowConfiguration.getBounds(), getBounds())) {
mStartingData.mResizedFromTransfer = true;
// Lock the starting window to task, so it won't resize from transfer anymore.
mActivityRecord.associateStartingWindowWithTaskIfNeeded();
diff --git a/services/core/java/com/android/server/wm/utils/RegionUtils.java b/services/core/java/com/android/server/wm/utils/RegionUtils.java
index ff23145fc6b9..6c5da175644b 100644
--- a/services/core/java/com/android/server/wm/utils/RegionUtils.java
+++ b/services/core/java/com/android/server/wm/utils/RegionUtils.java
@@ -92,4 +92,9 @@ public class RegionUtils {
}
return area;
}
+
+ /** Returns whether the sizes between the two Rects are equal. */
+ public static boolean sizeEquals(Rect a, Rect b) {
+ return a.width() == b.width() && a.height() == b.height();
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/security/advancedprotection/AdvancedProtectionServiceTest.java b/services/tests/servicestests/src/com/android/server/security/advancedprotection/AdvancedProtectionServiceTest.java
index e97b48cb3489..24bf6ca507e6 100644
--- a/services/tests/servicestests/src/com/android/server/security/advancedprotection/AdvancedProtectionServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/security/advancedprotection/AdvancedProtectionServiceTest.java
@@ -16,6 +16,8 @@
package com.android.server.security.advancedprotection;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.collection.IsIterableContainingInAnyOrder.containsInAnyOrder;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
@@ -28,15 +30,20 @@ import android.os.RemoteException;
import android.os.test.FakePermissionEnforcer;
import android.os.test.TestLooper;
import android.provider.Settings;
+import android.security.advancedprotection.AdvancedProtectionFeature;
import android.security.advancedprotection.IAdvancedProtectionCallback;
+import androidx.annotation.NonNull;
+
import com.android.server.security.advancedprotection.features.AdvancedProtectionHook;
+import com.android.server.security.advancedprotection.features.AdvancedProtectionProvider;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
+import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
@SuppressLint("VisibleForTests")
@@ -47,6 +54,7 @@ public class AdvancedProtectionServiceTest {
private Context mContext;
private AdvancedProtectionService.AdvancedProtectionStore mStore;
private TestLooper mLooper;
+ AdvancedProtectionFeature mFeature = new AdvancedProtectionFeature("test-id");
@Before
public void setup() throws Settings.SettingNotFoundException {
@@ -70,8 +78,9 @@ public class AdvancedProtectionServiceTest {
};
mLooper = new TestLooper();
+
mService = new AdvancedProtectionService(mContext, mStore, mLooper.getLooper(),
- mPermissionEnforcer, null);
+ mPermissionEnforcer, null, null);
}
@Test
@@ -93,6 +102,12 @@ public class AdvancedProtectionServiceTest {
AtomicBoolean callbackCaptor = new AtomicBoolean(false);
AdvancedProtectionHook hook =
new AdvancedProtectionHook(mContext, true) {
+ @NonNull
+ @Override
+ public AdvancedProtectionFeature getFeature() {
+ return mFeature;
+ }
+
@Override
public boolean isAvailable() {
return true;
@@ -105,7 +120,7 @@ public class AdvancedProtectionServiceTest {
};
mService = new AdvancedProtectionService(mContext, mStore, mLooper.getLooper(),
- mPermissionEnforcer, hook);
+ mPermissionEnforcer, hook, null);
mService.setAdvancedProtectionEnabled(true);
mLooper.dispatchNext();
@@ -117,6 +132,12 @@ public class AdvancedProtectionServiceTest {
AtomicBoolean callbackCalledCaptor = new AtomicBoolean(false);
AdvancedProtectionHook hook =
new AdvancedProtectionHook(mContext, true) {
+ @NonNull
+ @Override
+ public AdvancedProtectionFeature getFeature() {
+ return mFeature;
+ }
+
@Override
public boolean isAvailable() {
return false;
@@ -129,7 +150,8 @@ public class AdvancedProtectionServiceTest {
};
mService = new AdvancedProtectionService(mContext, mStore, mLooper.getLooper(),
- mPermissionEnforcer, hook);
+ mPermissionEnforcer, hook, null);
+
mService.setAdvancedProtectionEnabled(true);
mLooper.dispatchNext();
assertFalse(callbackCalledCaptor.get());
@@ -140,6 +162,12 @@ public class AdvancedProtectionServiceTest {
AtomicBoolean callbackCalledCaptor = new AtomicBoolean(false);
AdvancedProtectionHook hook =
new AdvancedProtectionHook(mContext, true) {
+ @NonNull
+ @Override
+ public AdvancedProtectionFeature getFeature() {
+ return mFeature;
+ }
+
@Override
public boolean isAvailable() {
return true;
@@ -152,7 +180,7 @@ public class AdvancedProtectionServiceTest {
};
mService = new AdvancedProtectionService(mContext, mStore, mLooper.getLooper(),
- mPermissionEnforcer, hook);
+ mPermissionEnforcer, hook, null);
mService.setAdvancedProtectionEnabled(true);
mLooper.dispatchNext();
assertTrue(callbackCalledCaptor.get());
@@ -208,6 +236,66 @@ public class AdvancedProtectionServiceTest {
assertFalse(callbackCalledCaptor.get());
}
+ @Test
+ public void testGetFeatures() {
+ AdvancedProtectionFeature feature1 = new AdvancedProtectionFeature("id-1");
+ AdvancedProtectionFeature feature2 = new AdvancedProtectionFeature("id-2");
+ AdvancedProtectionHook hook = new AdvancedProtectionHook(mContext, true) {
+ @NonNull
+ @Override
+ public AdvancedProtectionFeature getFeature() {
+ return feature1;
+ }
+
+ @Override
+ public boolean isAvailable() {
+ return true;
+ }
+ };
+
+ AdvancedProtectionProvider provider = new AdvancedProtectionProvider() {
+ @Override
+ public List<AdvancedProtectionFeature> getFeatures() {
+ return List.of(feature2);
+ }
+ };
+
+ mService = new AdvancedProtectionService(mContext, mStore, mLooper.getLooper(),
+ mPermissionEnforcer, hook, provider);
+ List<AdvancedProtectionFeature> features = mService.getAdvancedProtectionFeatures();
+ assertThat(features, containsInAnyOrder(feature1, feature2));
+ }
+
+ @Test
+ public void testGetFeatures_featureNotAvailable() {
+ AdvancedProtectionFeature feature1 = new AdvancedProtectionFeature("id-1");
+ AdvancedProtectionFeature feature2 = new AdvancedProtectionFeature("id-2");
+ AdvancedProtectionHook hook = new AdvancedProtectionHook(mContext, true) {
+ @NonNull
+ @Override
+ public AdvancedProtectionFeature getFeature() {
+ return feature1;
+ }
+
+ @Override
+ public boolean isAvailable() {
+ return false;
+ }
+ };
+
+ AdvancedProtectionProvider provider = new AdvancedProtectionProvider() {
+ @Override
+ public List<AdvancedProtectionFeature> getFeatures() {
+ return List.of(feature2);
+ }
+ };
+
+ mService = new AdvancedProtectionService(mContext, mStore, mLooper.getLooper(),
+ mPermissionEnforcer, hook, provider);
+ List<AdvancedProtectionFeature> features = mService.getAdvancedProtectionFeatures();
+ assertThat(features, containsInAnyOrder(feature2));
+ }
+
@Test
public void testSetProtection_withoutPermission() {
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 b99ab0595c9d..ac021e122998 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -112,6 +112,7 @@ import static android.service.notification.Condition.SOURCE_CONTEXT;
import static android.service.notification.Condition.SOURCE_USER_ACTION;
import static android.service.notification.Condition.STATE_TRUE;
import static android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION;
+import static android.service.notification.Flags.FLAG_NOTIFICATION_CONVERSATION_CHANNEL_MANAGEMENT;
import static android.service.notification.Flags.FLAG_NOTIFICATION_FORCE_GROUPING;
import static android.service.notification.Flags.FLAG_REDACT_SENSITIVE_NOTIFICATIONS_FROM_UNTRUSTED_LISTENERS;
import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_ALERTING;
@@ -265,6 +266,7 @@ import android.os.WorkSource;
import android.permission.PermissionManager;
import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
+import android.platform.test.annotations.RequiresFlagsEnabled;
import android.platform.test.flag.junit.FlagsParameterization;
import android.platform.test.flag.junit.SetFlagsRule;
import android.platform.test.rule.LimitDevicesRule;
@@ -485,6 +487,15 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
NotificationChannel mMinChannel = new NotificationChannel("min", "min", IMPORTANCE_MIN);
+ private final NotificationChannel mParentChannel =
+ new NotificationChannel(PARENT_CHANNEL_ID, "parentName", IMPORTANCE_DEFAULT);
+ private final NotificationChannel mConversationChannel =
+ new NotificationChannel(CONVERSATION_CHANNEL_ID, "conversationName", IMPORTANCE_DEFAULT);
+
+ private static final String PARENT_CHANNEL_ID = "parentChannelId";
+ private static final String CONVERSATION_CHANNEL_ID = "conversationChannelId";
+ private static final String CONVERSATION_ID = "conversationId";
+
private static final int NOTIFICATION_LOCATION_UNKNOWN = 0;
private static final String VALID_CONVO_SHORTCUT_ID = "shortcut";
@@ -4672,8 +4683,161 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
verify(mAmi).hasForegroundServiceNotification(anyString(), anyInt(), anyString());
}
+ private void setUpChannelsForConversationChannelTest() throws RemoteException {
+ when(mPreferencesHelper.getNotificationChannel(
+ eq(mPkg), eq(mUid), eq(PARENT_CHANNEL_ID), eq(false)))
+ .thenReturn(mParentChannel);
+ when(mPreferencesHelper.getConversationNotificationChannel(
+ eq(mPkg), eq(mUid), eq(PARENT_CHANNEL_ID), eq(CONVERSATION_ID), eq(false), eq(false)))
+ .thenReturn(mConversationChannel);
+ when(mPackageManager.getPackageUid(mPkg, 0, mUserId)).thenReturn(mUid);
+ }
+
+ @Test
+ @RequiresFlagsEnabled(FLAG_NOTIFICATION_CONVERSATION_CHANNEL_MANAGEMENT)
+ public void createConversationChannelForPkgFromPrivilegedListener_cdm_success() throws Exception {
+ // Set up cdm
+ mService.setPreferencesHelper(mPreferencesHelper);
+ when(mCompanionMgr.getAssociations(mPkg, mUserId))
+ .thenReturn(singletonList(mock(AssociationInfo.class)));
+
+ // Set up parent channel
+ setUpChannelsForConversationChannelTest();
+ final NotificationChannel parentChannelCopy = mParentChannel.copy();
+
+ NotificationChannel createdChannel =
+ mBinderService.createConversationNotificationChannelForPackageFromPrivilegedListener(
+ null, mPkg, mUser, PARENT_CHANNEL_ID, CONVERSATION_ID);
+
+ // Verify that a channel is created and a copied channel is returned.
+ verify(mPreferencesHelper, times(1)).createNotificationChannel(
+ eq(mPkg), eq(mUid), any(), anyBoolean(), anyBoolean(),
+ eq(mUid), anyBoolean());
+ assertThat(createdChannel).isNotSameInstanceAs(mConversationChannel);
+ assertThat(createdChannel).isEqualTo(mConversationChannel);
+
+ // Verify that the channel creation is not directly use the parent channel.
+ verify(mPreferencesHelper, never()).createNotificationChannel(
+ anyString(), anyInt(), eq(mParentChannel), anyBoolean(), anyBoolean(),
+ anyInt(), anyBoolean());
+
+ // Verify that the content of parent channel is not changed.
+ assertThat(parentChannelCopy).isEqualTo(mParentChannel);
+ }
+
+ @Test
+ @RequiresFlagsEnabled(FLAG_NOTIFICATION_CONVERSATION_CHANNEL_MANAGEMENT)
+ public void createConversationChannelForPkgFromPrivilegedListener_cdm_noAccess() throws Exception {
+ // Set up cdm without access
+ mService.setPreferencesHelper(mPreferencesHelper);
+ when(mCompanionMgr.getAssociations(mPkg, mUserId))
+ .thenReturn(emptyList());
+
+ // Set up parent channel
+ setUpChannelsForConversationChannelTest();
+
+ try {
+ mBinderService.createConversationNotificationChannelForPackageFromPrivilegedListener(
+ null, mPkg, mUser, "parentId", "conversationId");
+ fail("listeners that don't have a companion device shouldn't be able to call this");
+ } catch (SecurityException e) {
+ // pass
+ }
+
+ verify(mPreferencesHelper, never()).createNotificationChannel(
+ anyString(), anyInt(), any(), anyBoolean(), anyBoolean(),
+ anyInt(), anyBoolean());
+ }
+
+ @Test
+ @RequiresFlagsEnabled(FLAG_NOTIFICATION_CONVERSATION_CHANNEL_MANAGEMENT)
+ public void createConversationChannelForPkgFromPrivilegedListener_assistant_success() throws Exception {
+ // Set up assistant
+ mService.setPreferencesHelper(mPreferencesHelper);
+ when(mCompanionMgr.getAssociations(mPkg, mUserId))
+ .thenReturn(emptyList());
+ when(mAssistants.isServiceTokenValidLocked(any())).thenReturn(true);
+
+ // Set up parent channel
+ setUpChannelsForConversationChannelTest();
+ final NotificationChannel parentChannelCopy = mParentChannel.copy();
+
+ NotificationChannel createdChannel =
+ mBinderService.createConversationNotificationChannelForPackageFromPrivilegedListener(
+ null, mPkg, mUser, PARENT_CHANNEL_ID, CONVERSATION_ID);
+
+ // Verify that a channel is created and a copied channel is returned.
+ verify(mPreferencesHelper, times(1)).createNotificationChannel(
+ eq(mPkg), eq(mUid), any(), anyBoolean(), anyBoolean(),
+ eq(mUid), anyBoolean());
+ assertThat(createdChannel).isNotSameInstanceAs(mConversationChannel);
+ assertThat(createdChannel).isEqualTo(mConversationChannel);
+
+ // Verify that the channel creation is not directly use the parent channel.
+ verify(mPreferencesHelper, never()).createNotificationChannel(
+ anyString(), anyInt(), eq(mParentChannel), anyBoolean(), anyBoolean(),
+ anyInt(), anyBoolean());
+
+ // Verify that the content of parent channel is not changed.
+ assertThat(parentChannelCopy).isEqualTo(mParentChannel);
+ }
+
+ @Test
+ @RequiresFlagsEnabled(FLAG_NOTIFICATION_CONVERSATION_CHANNEL_MANAGEMENT)
+ public void createConversationChannelForPkgFromPrivilegedListener_assistant_noAccess() throws Exception {
+ // Set up assistant without access
+ mService.setPreferencesHelper(mPreferencesHelper);
+ when(mCompanionMgr.getAssociations(mPkg, mUserId))
+ .thenReturn(emptyList());
+ when(mAssistants.isServiceTokenValidLocked(any())).thenReturn(false);
+
+ // Set up parent channel
+ setUpChannelsForConversationChannelTest();
+
+ try {
+ mBinderService.createConversationNotificationChannelForPackageFromPrivilegedListener(
+ null, mPkg, mUser, "parentId", "conversationId");
+ fail("listeners that don't have a companion device shouldn't be able to call this");
+ } catch (SecurityException e) {
+ // pass
+ }
+
+ verify(mPreferencesHelper, never()).createNotificationChannel(
+ anyString(), anyInt(), any(), anyBoolean(), anyBoolean(),
+ anyInt(), anyBoolean());
+ }
+
+ @Test
+ @RequiresFlagsEnabled(FLAG_NOTIFICATION_CONVERSATION_CHANNEL_MANAGEMENT)
+ public void createConversationChannelForPkgFromPrivilegedListener_badUser() throws Exception {
+ // Set up bad user
+ mService.setPreferencesHelper(mPreferencesHelper);
+ when(mCompanionMgr.getAssociations(mPkg, mUserId))
+ .thenReturn(singletonList(mock(AssociationInfo.class)));
+ mListener = mock(ManagedServices.ManagedServiceInfo.class);
+ mListener.component = new ComponentName(mPkg, mPkg);
+ when(mListener.enabledAndUserMatches(anyInt())).thenReturn(false);
+ when(mListeners.checkServiceTokenLocked(any())).thenReturn(mListener);
+
+ // Set up parent channel
+ setUpChannelsForConversationChannelTest();
+
+ try {
+ mBinderService.createConversationNotificationChannelForPackageFromPrivilegedListener(
+ null, mPkg, mUser, "parentId", "conversationId");
+ fail("listener getting channels from a user they cannot see");
+ } catch (SecurityException e) {
+ // pass
+ }
+
+ verify(mPreferencesHelper, never()).createNotificationChannel(
+ anyString(), anyInt(), any(), anyBoolean(), anyBoolean(),
+ anyInt(), anyBoolean());
+ }
+
@Test
- public void testUpdateNotificationChannelFromPrivilegedListener_success() throws Exception {
+ public void updateNotificationChannelFromPrivilegedListener_cdm_success() throws Exception {
+
mService.setPreferencesHelper(mPreferencesHelper);
when(mCompanionMgr.getAssociations(mPkg, mUserId))
.thenReturn(singletonList(mock(AssociationInfo.class)));
@@ -4693,7 +4857,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
}
@Test
- public void testUpdateNotificationChannelFromPrivilegedListener_noAccess() throws Exception {
+ public void updateNotificationChannelFromPrivilegedListener_cdm_noAccess() throws Exception {
mService.setPreferencesHelper(mPreferencesHelper);
when(mCompanionMgr.getAssociations(mPkg, mUserId))
.thenReturn(emptyList());
@@ -4715,7 +4879,51 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
}
@Test
- public void testUpdateNotificationChannelFromPrivilegedListener_badUser() throws Exception {
+ public void updateNotificationChannelFromPrivilegedListener_assistant_success() throws Exception {
+ mService.setPreferencesHelper(mPreferencesHelper);
+ when(mCompanionMgr.getAssociations(mPkg, mUserId))
+ .thenReturn(emptyList());
+ when(mAssistants.isServiceTokenValidLocked(any())).thenReturn(true);
+ when(mPreferencesHelper.getNotificationChannel(eq(mPkg), anyInt(),
+ eq(mTestNotificationChannel.getId()), anyBoolean()))
+ .thenReturn(mTestNotificationChannel);
+
+ mBinderService.updateNotificationChannelFromPrivilegedListener(
+ null, mPkg, Process.myUserHandle(), mTestNotificationChannel);
+
+ verify(mPreferencesHelper, times(1)).updateNotificationChannel(
+ anyString(), anyInt(), any(), anyBoolean(), anyInt(), anyBoolean());
+
+ verify(mListeners, never()).notifyNotificationChannelChanged(eq(mPkg),
+ eq(Process.myUserHandle()), eq(mTestNotificationChannel),
+ eq(NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_UPDATED));
+ }
+
+ @Test
+ public void updateNotificationChannelFromPrivilegedListener_assistant_noAccess() throws Exception {
+ mService.setPreferencesHelper(mPreferencesHelper);
+ when(mCompanionMgr.getAssociations(mPkg, mUserId))
+ .thenReturn(emptyList());
+ when(mAssistants.isServiceTokenValidLocked(any())).thenReturn(false);
+
+ try {
+ mBinderService.updateNotificationChannelFromPrivilegedListener(
+ null, mPkg, Process.myUserHandle(), mTestNotificationChannel);
+ fail("listeners that don't have a companion device shouldn't be able to call this");
+ } catch (SecurityException e) {
+ // pass
+ }
+
+ verify(mPreferencesHelper, never()).updateNotificationChannel(
+ anyString(), anyInt(), any(), anyBoolean(), anyInt(), anyBoolean());
+
+ verify(mListeners, never()).notifyNotificationChannelChanged(eq(mPkg),
+ eq(Process.myUserHandle()), eq(mTestNotificationChannel),
+ eq(NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_UPDATED));
+ }
+
+ @Test
+ public void updateNotificationChannelFromPrivilegedListener_badUser() throws Exception {
mService.setPreferencesHelper(mPreferencesHelper);
when(mCompanionMgr.getAssociations(mPkg, mUserId))
.thenReturn(singletonList(mock(AssociationInfo.class)));
@@ -4741,7 +4949,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
}
@Test
- public void testUpdateNotificationChannelFromPrivilegedListener_noSoundUriPermission()
+ public void updateNotificationChannelFromPrivilegedListener_noSoundUriPermission()
throws Exception {
mService.setPreferencesHelper(mPreferencesHelper);
when(mCompanionMgr.getAssociations(mPkg, mUserId))
@@ -4773,7 +4981,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
}
@Test
- public void testUpdateNotificationChannelFromPrivilegedListener_noSoundUriPermission_sameSound()
+ public void updateNotificationChannelFromPrivilegedListener_noSoundUriPermission_sameSound()
throws Exception {
mService.setPreferencesHelper(mPreferencesHelper);
when(mCompanionMgr.getAssociations(mPkg, mUserId))
@@ -4805,7 +5013,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
@Test
public void
- testUpdateNotificationChannelFromPrivilegedListener_oldSoundNoUriPerm_newSoundHasUriPerm()
+ updateNotificationChannelFromPrivilegedListener_oldSoundNoUriPerm_newSoundHasUriPerm()
throws Exception {
mService.setPreferencesHelper(mPreferencesHelper);
when(mCompanionMgr.getAssociations(mPkg, mUserId))
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 bdf146fa94a8..3236f9501324 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java
@@ -528,8 +528,8 @@ public class ZenModeConfigTest extends UiServiceTestCase {
String xml = "<zen version=\"12\">\n"
+ "<allow calls=\"true\" repeatCallers=\"true\" messages=\"true\""
+ " reminders=\"false\" events=\"false\" callsFrom=\"2\" messagesFrom=\"2\""
- + " alarms=\"true\" media=\"true\" system=\"false\" convos=\"true\" convosFrom=\"2\""
- + " priorityChannelsAllowed=\"true\" />\n"
+ + " alarms=\"true\" media=\"true\" system=\"false\" convos=\"true\""
+ + " convosFrom=\"2\" priorityChannelsAllowed=\"true\" />\n"
+ "<disallow visualEffects=\"157\" />\n"
+ "<manual enabled=\"true\" zen=\"1\" creationTime=\"0\" modified=\"false\" />\n"
+ "<state areChannelsBypassingDnd=\"true\" />\n"
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatActivityRobot.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatActivityRobot.java
index 3742249bdffe..b8cfa7c39464 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppCompatActivityRobot.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatActivityRobot.java
@@ -143,6 +143,10 @@ class AppCompatActivityRobot {
doReturn(naturalOrientation).when(mDisplayContent).getNaturalOrientation();
}
+ void setDisplayIgnoreActivitySizeRestrictions(boolean enabled) {
+ doReturn(enabled).when(mDisplayContent).isDisplayIgnoreActivitySizeRestrictions();
+ }
+
void configureTaskBounds(@NonNull Rect taskBounds) {
doReturn(taskBounds).when(mTaskStack.top()).getBounds();
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatAspectRatioOverridesTest.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatAspectRatioOverridesTest.java
index b83911337c5c..14ef913e28db 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppCompatAspectRatioOverridesTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatAspectRatioOverridesTest.java
@@ -17,9 +17,12 @@
package com.android.server.wm;
import static android.content.pm.ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_3_2;
import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_FULLSCREEN;
+import static android.view.Surface.ROTATION_90;
import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE;
+import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_ORIENTATION_OVERRIDE;
import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_FULLSCREEN_OVERRIDE;
import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_OVERRIDE;
@@ -29,10 +32,13 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import android.compat.testing.PlatformCompatChangeRule;
+import android.platform.test.annotations.EnableFlags;
import android.platform.test.annotations.Presubmit;
import androidx.annotation.NonNull;
+import com.android.window.flags.Flags;
+
import libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges;
import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges;
@@ -288,6 +294,93 @@ public class AppCompatAspectRatioOverridesTest extends WindowTestsBase {
});
}
+ @Test
+ @EnableFlags(Flags.FLAG_VDM_FORCE_APP_UNIVERSAL_RESIZABLE_API)
+ public void testHasFullscreenOverride_displayIgnoreActivitySizeRestrictionsTrue() {
+ runTestScenario((robot) -> {
+ robot.applyOnActivity((a) -> {
+ a.setDisplayIgnoreActivitySizeRestrictions(true);
+ a.createActivityWithComponent();
+ });
+
+ robot.checkHasFullscreenOverride(true);
+ });
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_VDM_FORCE_APP_UNIVERSAL_RESIZABLE_API)
+ public void testHasFullscreenOverride_displayIgnoreActivitySizeRestrictionsFalse() {
+ runTestScenario((robot) -> {
+ robot.applyOnActivity((a) -> {
+ a.setDisplayIgnoreActivitySizeRestrictions(false);
+ a.createActivityWithComponent();
+ });
+
+ robot.checkHasFullscreenOverride(false);
+ });
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_VDM_FORCE_APP_UNIVERSAL_RESIZABLE_API)
+ public void testPropFalse_displayIgnoreActivitySizeRestrictionsTrue_notOverridden() {
+ runTestScenario((robot) -> {
+ robot.prop().disable(PROPERTY_COMPAT_ALLOW_ORIENTATION_OVERRIDE);
+ robot.applyOnActivity((a) -> {
+ a.setDisplayIgnoreActivitySizeRestrictions(true);
+ a.createActivityWithComponent();
+ });
+
+ robot.checkHasFullscreenOverride(false);
+ });
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_VDM_FORCE_APP_UNIVERSAL_RESIZABLE_API)
+ public void testPropTrue_displayIgnoreActivitySizeRestrictionsFalse_notOverridden() {
+ runTestScenario((robot) -> {
+ robot.prop().enable(PROPERTY_COMPAT_ALLOW_ORIENTATION_OVERRIDE);
+ robot.applyOnActivity((a) -> {
+ a.setDisplayIgnoreActivitySizeRestrictions(false);
+ a.createActivityWithComponent();
+ });
+
+ robot.checkHasFullscreenOverride(false);
+ });
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_VDM_FORCE_APP_UNIVERSAL_RESIZABLE_API)
+ public void testNotInSizeCompatMode_displayIgnoreActivitySizeRestrictionsTrue() {
+ runTestScenario((robot) -> {
+ robot.applyOnActivity((a) -> {
+ a.createActivityWithComponent();
+ a.setDisplayIgnoreActivitySizeRestrictions(true);
+ a.configureTopActivity(/* minAspect */ -1f, /* maxAspect */-1f,
+ SCREEN_ORIENTATION_LANDSCAPE, true);
+ a.rotateDisplayForTopActivity(ROTATION_90);
+
+ a.checkTopActivityInSizeCompatMode(false);
+ });
+ });
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_VDM_FORCE_APP_UNIVERSAL_RESIZABLE_API)
+ public void testInSizeCompatMode_displayIgnoreActivitySizeRestrictionsFalse() {
+ runTestScenario((robot) -> {
+ robot.applyOnActivity((a) -> {
+ a.createActivityWithComponent();
+ a.setIgnoreOrientationRequest(true);
+ a.setDisplayIgnoreActivitySizeRestrictions(false);
+ a.configureTopActivity(/* minAspect */ -1f, /* maxAspect */-1f,
+ SCREEN_ORIENTATION_LANDSCAPE, true);
+ a.rotateDisplayForTopActivity(ROTATION_90);
+
+ a.checkTopActivityInSizeCompatMode(true);
+ });
+ });
+ }
+
/**
* Runs a test scenario providing a Robot.
*/
@@ -366,6 +459,11 @@ public class AppCompatAspectRatioOverridesTest extends WindowTestsBase {
}
}
+ void checkHasFullscreenOverride(boolean expected) {
+ assertEquals(expected,
+ getTopActivityAppCompatAspectRatioOverrides().hasFullscreenOverride());
+ }
+
private AppCompatAspectRatioOverrides getTopActivityAppCompatAspectRatioOverrides() {
return activity().top().mAppCompatController.getAppCompatAspectRatioOverrides();
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerTests.java
index 7bc9f301bcd6..db3ce0bc98eb 100644
--- a/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerTests.java
@@ -194,6 +194,7 @@ public class BackgroundActivityStartControllerTests {
mService.mTaskSupervisor = mSupervisor;
mService.mContext = mContext;
setViaReflection(mService, "mActiveUids", mActiveUids);
+ setViaReflection(mService, "mGlobalLock", new WindowManagerGlobalLock());
Mockito.when(mService.getPackageManagerInternalLocked()).thenReturn(
mPackageManagerInternal);
mService.mRootWindowContainer = mRootWindowContainer;
diff --git a/telephony/common/com/android/internal/telephony/TelephonyPermissions.java b/telephony/common/com/android/internal/telephony/TelephonyPermissions.java
index 4ccbc32c4b54..66d75f780fbe 100644
--- a/telephony/common/com/android/internal/telephony/TelephonyPermissions.java
+++ b/telephony/common/com/android/internal/telephony/TelephonyPermissions.java
@@ -907,29 +907,6 @@ public final class TelephonyPermissions {
}
/**
- * Ensure the caller (or self, if not processing an IPC) has
- * {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE} or
- * {@link android.Manifest.permission#READ_PHONE_NUMBERS}.
- *
- * @throws SecurityException if the caller does not have the required permission/privileges
- */
- @RequiresPermission(anyOf = {
- android.Manifest.permission.READ_PHONE_NUMBERS,
- android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE
- })
- public static boolean checkCallingOrSelfReadPrivilegedPhoneStatePermissionOrReadPhoneNumber(
- Context context, int subId, String callingPackage, @Nullable String callingFeatureId,
- String message) {
- if (!SubscriptionManager.isValidSubscriptionId(subId)) {
- return false;
- }
- return (context.checkCallingOrSelfPermission(
- Manifest.permission.READ_PRIVILEGED_PHONE_STATE) == PERMISSION_GRANTED
- || checkCallingOrSelfReadPhoneNumber(context, subId, callingPackage,
- callingFeatureId, message));
- }
-
- /**
* @return true if the specified {@code uid} is for a system or phone process, no matter if runs
* as system user or not.
*/
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index fad59f8bb37b..6f2c8623fd71 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -133,6 +133,7 @@ import com.android.internal.telephony.OperatorInfo;
import com.android.internal.telephony.PhoneConstants;
import com.android.internal.telephony.RILConstants;
import com.android.internal.telephony.flags.Flags;
+import com.android.internal.telephony.uicc.IccUtils;
import com.android.internal.telephony.util.TelephonyUtils;
import com.android.telephony.Rlog;
@@ -6282,14 +6283,17 @@ public class TelephonyManager {
* The contents of the file is a <b>Ip Multimedia Service Private User Identity</b> of the user
* as defined in the section 4.2.2 of 3GPP TS 131 103.
*
- * @return IMPI (IMS private user identity) of type string.
+ * @return IMPI (IMS private user identity) of type string or null if the IMPI isn't present
+ * on the ISIM.
* @throws IllegalStateException in case the ISIM has’t been loaded
* @throws SecurityException if the caller does not have the required permission/privileges
* @hide
*/
- @NonNull
+ @FlaggedApi(Flags.FLAG_SUPPORT_ISIM_RECORD)
+ @SystemApi
@RequiresPermission(android.Manifest.permission.USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER)
@RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
+ @Nullable
public String getImsPrivateUserIdentity() {
try {
IPhoneSubInfo info = getSubscriberInfoService();
@@ -6370,6 +6374,9 @@ public class TelephonyManager {
* The contents of the file are <b>Ip Multimedia Service Public User Identities</b> of the user
* as defined in the section 4.2.4 of 3GPP TS 131 103. It contains one or more records.
*
+ * Requires that the calling app has READ_PRIVILEGED_PHONE_STATE permission or carrier
+ * privileges.
+ *
* @return List of public user identities of type android.net.Uri or empty list if
* EF_IMPU is not available.
* @throws IllegalStateException in case the ISIM hasn’t been loaded
@@ -6378,18 +6385,18 @@ public class TelephonyManager {
* {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
* @hide
*/
- @NonNull
- @RequiresPermission(anyOf = {android.Manifest.permission.READ_PHONE_NUMBERS,
- android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE})
+ @FlaggedApi(Flags.FLAG_SUPPORT_ISIM_RECORD)
+ @SystemApi
+ @RequiresPermission(value = Manifest.permission.READ_PRIVILEGED_PHONE_STATE, conditional = true)
@RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
+ @NonNull
public List<Uri> getImsPublicUserIdentities() {
try {
IPhoneSubInfo info = getSubscriberInfoService();
if (info == null) {
throw new RuntimeException("IMPU error: Subscriber Info is null");
}
- return info.getImsPublicUserIdentities(getSubId(), getOpPackageName(),
- getAttributionTag());
+ return info.getImsPublicUserIdentities(getSubId(), getOpPackageName());
} catch (IllegalArgumentException | NullPointerException ex) {
Rlog.e(TAG, "getImsPublicUserIdentities Exception = " + ex);
} catch (RemoteException ex) {
@@ -8684,7 +8691,10 @@ public class TelephonyManager {
* @return an array of PCSCF strings with one PCSCF per string, or null if
* not present or not loaded
* @hide
+ * @deprecated use {@link #getImsPcscfAddresses()} instead.
*/
+ @Deprecated
+ @FlaggedApi(Flags.FLAG_SUPPORT_ISIM_RECORD)
@UnsupportedAppUsage
public String[] getIsimPcscf() {
try {
@@ -8701,6 +8711,40 @@ public class TelephonyManager {
}
}
+ /**
+ * Returns the IMS Proxy Call Session Control Function(P-CSCF) that were loaded from the ISIM.
+ *
+ * Requires that the calling app has READ_PRIVILEGED_PHONE_STATE permission or carrier
+ * privileges.
+ *
+ * @return List of P-CSCF address strings or empty list if not available.
+ * @throws IllegalStateException in case the ISIM hasn’t been loaded
+ * @throws SecurityException if the caller does not have the required permission/privilege
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+ * @hide
+ */
+ @FlaggedApi(Flags.FLAG_SUPPORT_ISIM_RECORD)
+ @SystemApi
+ @RequiresPermission(value = Manifest.permission.READ_PRIVILEGED_PHONE_STATE, conditional = true)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
+ @NonNull
+ public List<String> getImsPcscfAddresses() {
+ try {
+ IPhoneSubInfo info = getSubscriberInfoService();
+ if (info == null) {
+ throw new RuntimeException("P-CSCF error: Subscriber Info is null");
+ }
+ return info.getImsPcscfAddresses(getSubId(), getOpPackageName());
+ } catch (IllegalArgumentException | NullPointerException ex) {
+ Rlog.e(TAG, "getImsPcscfAddresses Exception = " + ex);
+ } catch (RemoteException ex) {
+ Rlog.e(TAG, "getImsPcscfAddresses Exception = " + ex);
+ ex.rethrowAsRuntimeException();
+ }
+ return Collections.EMPTY_LIST;
+ }
+
/** UICC application type is unknown or not specified */
public static final int APPTYPE_UNKNOWN = PhoneConstants.APPTYPE_UNKNOWN;
/** UICC application type is SIM */
@@ -8934,8 +8978,10 @@ public class TelephonyManager {
* @throws UnsupportedOperationException If the device does not have
* {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
* @hide
+ * @deprecated Use {@link #getSimServiceTable(int, Executor, OutcomeReceiver)} instead.
*/
-
+ @Deprecated
+ @FlaggedApi(Flags.FLAG_SUPPORT_ISIM_RECORD)
@Nullable
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
@RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
@@ -8963,6 +9009,55 @@ public class TelephonyManager {
}
/**
+ * Fetches the sim service table from the EFUST/EFIST based on the application type
+ * {@link #APPTYPE_USIM} or {@link #APPTYPE_ISIM}.
+ * The USIM service table EF is described in as per Section 4.2.8 of 3GPP TS 31.102.
+ * The ISIM service table EF is described in as per Section 4.2.7 of 3GPP TS 31.103.
+ *
+ * @param appType of type int of either {@link #APPTYPE_USIM} or {@link #APPTYPE_ISIM}.
+ * @param executor executor to run the callback on.
+ * @param callback callback object to which the result will be delivered.
+ * @hide
+ */
+ @FlaggedApi(Flags.FLAG_SUPPORT_ISIM_RECORD)
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
+ public void getSimServiceTable(@UiccAppType int appType,
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull OutcomeReceiver<byte[], Exception> callback) {
+ Objects.requireNonNull(executor);
+ Objects.requireNonNull(callback);
+
+ IPhoneSubInfo info = getSubscriberInfoService();
+ if (info == null) {
+ executor.execute(() -> callback.onError(
+ new RuntimeException("getSimServiceTable: Subscriber Info is null")));
+ return;
+ }
+
+ try {
+ String serviceTable;
+ if (appType == APPTYPE_ISIM) {
+ serviceTable = info.getIsimIst(getSubId());
+ } else if ((appType == APPTYPE_USIM)) {
+ serviceTable = info.getSimServiceTable(getSubId(), APPTYPE_USIM);
+ } else {
+ serviceTable = null;
+ }
+
+ if (serviceTable == null) {
+ executor.execute(() -> callback.onResult(new byte[0]));
+ } else {
+ byte[] simServiceTable = IccUtils.hexStringToBytes(serviceTable);
+ executor.execute(() -> callback.onResult(simServiceTable));
+ }
+ } catch (Exception ex) {
+ executor.execute(() -> callback.onError(ex));
+ }
+ }
+
+ /**
* Resets the {@link android.telephony.ims.ImsService} associated with the specified sim slot.
* Used by diagnostic apps to force the IMS stack to be disabled and re-enabled in an effort to
* recover from scenarios where the {@link android.telephony.ims.ImsService} gets in to a bad
diff --git a/telephony/java/android/telephony/ims/ImsCallSessionListener.java b/telephony/java/android/telephony/ims/ImsCallSessionListener.java
index 5946535ce3af..00d4bd09b7c4 100644
--- a/telephony/java/android/telephony/ims/ImsCallSessionListener.java
+++ b/telephony/java/android/telephony/ims/ImsCallSessionListener.java
@@ -16,6 +16,7 @@
package android.telephony.ims;
+import android.annotation.FlaggedApi;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -31,6 +32,7 @@ import android.telephony.ims.stub.ImsCallSessionImplBase.MediaStreamType;
import android.util.Log;
import com.android.ims.internal.IImsCallSession;
+import com.android.internal.telephony.flags.Flags;
import java.util.ArrayList;
import java.util.Objects;
@@ -802,8 +804,8 @@ public class ImsCallSessionListener {
/**
* Notifies the result of transfer request.
- * @hide
*/
+ @FlaggedApi(Flags.FLAG_SUPPORT_IMS_MMTEL_INTERFACE)
public void callSessionTransferred() {
try {
mListener.callSessionTransferred();
@@ -813,13 +815,13 @@ public class ImsCallSessionListener {
}
/**
- * Notifies the result of transfer request.
+ * Notifies the result of the transfer request failure.
*
* @param reasonInfo {@link ImsReasonInfo} containing a reason for the
* session transfer failure
- * @hide
*/
- public void callSessionTransferFailed(ImsReasonInfo reasonInfo) {
+ @FlaggedApi(Flags.FLAG_SUPPORT_IMS_MMTEL_INTERFACE)
+ public void callSessionTransferFailed(@NonNull ImsReasonInfo reasonInfo) {
try {
mListener.callSessionTransferFailed(reasonInfo);
} catch (RemoteException e) {
@@ -839,8 +841,8 @@ public class ImsCallSessionListener {
* @param bitsPerSecond This value is the bitrate requested by the other party UE through
* RTP CMR, RTCPAPP or TMMBR, and ImsStack converts this value to the MAC bitrate
* (defined in TS36.321, range: 0 ~ 8000 kbit/s).
- * @hide
*/
+ @FlaggedApi(Flags.FLAG_SUPPORT_IMS_MMTEL_INTERFACE)
public final void callSessionSendAnbrQuery(@MediaStreamType int mediaType,
@MediaStreamDirection int direction, @IntRange(from = 0) int bitsPerSecond) {
Log.d(TAG, "callSessionSendAnbrQuery in imscallsessonListener");
diff --git a/telephony/java/android/telephony/ims/feature/ConnectionFailureInfo.java b/telephony/java/android/telephony/ims/feature/ConnectionFailureInfo.java
index 88d9aae3e19d..81cddb79533c 100644
--- a/telephony/java/android/telephony/ims/feature/ConnectionFailureInfo.java
+++ b/telephony/java/android/telephony/ims/feature/ConnectionFailureInfo.java
@@ -16,12 +16,16 @@
package android.telephony.ims.feature;
+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 android.util.SparseArray;
+import com.android.internal.telephony.flags.Flags;
+
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -30,6 +34,8 @@ import java.lang.annotation.RetentionPolicy;
*
* @hide
*/
+@FlaggedApi(Flags.FLAG_SUPPORT_IMS_MMTEL_INTERFACE)
+@SystemApi
public final class ConnectionFailureInfo implements Parcelable {
/** @hide */
@@ -67,7 +73,7 @@ public final class ConnectionFailureInfo implements Parcelable {
public static final int REASON_RRC_TIMEOUT = 6;
/** Device currently not in service */
public static final int REASON_NO_SERVICE = 7;
- /** The PDN is no more active */
+ /** The PDN is no longer active */
public static final int REASON_PDN_NOT_AVAILABLE = 8;
/** Radio resource is busy with another subscription */
public static final int REASON_RF_BUSY = 9;
@@ -135,6 +141,8 @@ public final class ConnectionFailureInfo implements Parcelable {
/**
* @return the cause code from the network or modem specific to the failure.
+ * See 3GPP TS 24.401 Annex A (Cause values for EPS mobility management) and
+ * 3GPP TS 24.501 Annex A (Cause values for 5GS mobility management).
*/
public int getCauseCode() {
return mCauseCode;
diff --git a/telephony/java/android/telephony/ims/feature/ImsTrafficSessionCallback.java b/telephony/java/android/telephony/ims/feature/ImsTrafficSessionCallback.java
index 245ee1511d98..0029d492b811 100644
--- a/telephony/java/android/telephony/ims/feature/ImsTrafficSessionCallback.java
+++ b/telephony/java/android/telephony/ims/feature/ImsTrafficSessionCallback.java
@@ -16,20 +16,26 @@
package android.telephony.ims.feature;
+import android.annotation.FlaggedApi;
import android.annotation.NonNull;
+import android.annotation.SystemApi;
+
+import com.android.internal.telephony.flags.Flags;
/**
* A callback class used to receive the result of {@link MmTelFeature#startImsTrafficSession}.
* @hide
*/
+@FlaggedApi(Flags.FLAG_SUPPORT_IMS_MMTEL_INTERFACE)
+@SystemApi
public interface ImsTrafficSessionCallback {
/** The modem is ready to process the IMS traffic. */
void onReady();
/**
- * Notifies that any IMS traffic is not sent to network due to any failure
- * on cellular networks. IMS service shall call {@link MmTelFeature#stopImsTrafficSession()}
+ * Notifies that any IMS traffic can not be sent to the network due to the provided cellular
+ * network failure. IMS service shall call {@link MmTelFeature#stopImsTrafficSession()}
* when receiving this callback.
*
* @param info The information of the failure.
diff --git a/telephony/java/android/telephony/ims/feature/MmTelFeature.java b/telephony/java/android/telephony/ims/feature/MmTelFeature.java
index 3f02ae9e13df..c6b11d7b9c97 100644
--- a/telephony/java/android/telephony/ims/feature/MmTelFeature.java
+++ b/telephony/java/android/telephony/ims/feature/MmTelFeature.java
@@ -16,6 +16,8 @@
package android.telephony.ims.feature;
+import static com.android.internal.telephony.flags.Flags.FLAG_SUPPORT_IMS_MMTEL_INTERFACE;
+
import android.annotation.CallbackExecutor;
import android.annotation.FlaggedApi;
import android.annotation.IntDef;
@@ -964,6 +966,8 @@ public class MmTelFeature extends ImsFeature {
*
* @hide
*/
+ @FlaggedApi(FLAG_SUPPORT_IMS_MMTEL_INTERFACE)
+ @SystemApi
public static final int EPS_FALLBACK_REASON_NO_NETWORK_TRIGGER = 1;
/**
@@ -976,6 +980,8 @@ public class MmTelFeature extends ImsFeature {
*
* @hide
*/
+ @FlaggedApi(FLAG_SUPPORT_IMS_MMTEL_INTERFACE)
+ @SystemApi
public static final int EPS_FALLBACK_REASON_NO_NETWORK_RESPONSE = 2;
/** @hide */
@@ -1003,36 +1009,50 @@ public class MmTelFeature extends ImsFeature {
* Emergency call
* @hide
*/
+ @FlaggedApi(FLAG_SUPPORT_IMS_MMTEL_INTERFACE)
+ @SystemApi
public static final int IMS_TRAFFIC_TYPE_EMERGENCY = 0;
/**
* Emergency SMS
* @hide
*/
+ @FlaggedApi(FLAG_SUPPORT_IMS_MMTEL_INTERFACE)
+ @SystemApi
public static final int IMS_TRAFFIC_TYPE_EMERGENCY_SMS = 1;
/**
* Voice call
* @hide
*/
+ @FlaggedApi(FLAG_SUPPORT_IMS_MMTEL_INTERFACE)
+ @SystemApi
public static final int IMS_TRAFFIC_TYPE_VOICE = 2;
/**
* Video call
* @hide
*/
+ @FlaggedApi(FLAG_SUPPORT_IMS_MMTEL_INTERFACE)
+ @SystemApi
public static final int IMS_TRAFFIC_TYPE_VIDEO = 3;
/**
* SMS over IMS
* @hide
*/
+ @FlaggedApi(FLAG_SUPPORT_IMS_MMTEL_INTERFACE)
+ @SystemApi
public static final int IMS_TRAFFIC_TYPE_SMS = 4;
/**
* IMS registration and subscription for reg event package (signaling)
* @hide
*/
+ @FlaggedApi(FLAG_SUPPORT_IMS_MMTEL_INTERFACE)
+ @SystemApi
public static final int IMS_TRAFFIC_TYPE_REGISTRATION = 5;
/**
* Ut/XCAP (XML Configuration Access Protocol)
* @hide
*/
+ @FlaggedApi(FLAG_SUPPORT_IMS_MMTEL_INTERFACE)
+ @SystemApi
public static final int IMS_TRAFFIC_TYPE_UT_XCAP = 6;
/** @hide */
@@ -1046,11 +1066,15 @@ public class MmTelFeature extends ImsFeature {
* Indicates that the traffic is an incoming traffic.
* @hide
*/
+ @FlaggedApi(FLAG_SUPPORT_IMS_MMTEL_INTERFACE)
+ @SystemApi
public static final int IMS_TRAFFIC_DIRECTION_INCOMING = 0;
/**
* Indicates that the traffic is an outgoing traffic.
* @hide
*/
+ @FlaggedApi(FLAG_SUPPORT_IMS_MMTEL_INTERFACE)
+ @SystemApi
public static final int IMS_TRAFFIC_DIRECTION_OUTGOING = 1;
private IImsMmTelListener mListener;
@@ -1291,9 +1315,11 @@ public class MmTelFeature extends ImsFeature {
/**
* Triggers the EPS fallback procedure.
*
- * @param reason specifies the reason that causes EPS fallback.
+ * @param reason specifies the reason that EPS fallback was triggered.
* @hide
*/
+ @FlaggedApi(FLAG_SUPPORT_IMS_MMTEL_INTERFACE)
+ @SystemApi
public final void triggerEpsFallback(@EpsFallbackReason int reason) {
IImsMmTelListener listener = getListener();
if (listener == null) {
@@ -1344,6 +1370,8 @@ public class MmTelFeature extends ImsFeature {
*
* @hide
*/
+ @FlaggedApi(FLAG_SUPPORT_IMS_MMTEL_INTERFACE)
+ @SystemApi
public final void startImsTrafficSession(@ImsTrafficType int trafficType,
@AccessNetworkConstants.RadioAccessNetworkType int accessNetworkType,
@ImsTrafficDirection int trafficDirection,
@@ -1387,6 +1415,8 @@ public class MmTelFeature extends ImsFeature {
*
* @hide
*/
+ @FlaggedApi(FLAG_SUPPORT_IMS_MMTEL_INTERFACE)
+ @SystemApi
public final void modifyImsTrafficSession(
@AccessNetworkConstants.RadioAccessNetworkType int accessNetworkType,
@NonNull ImsTrafficSessionCallback callback) {
@@ -1417,6 +1447,8 @@ public class MmTelFeature extends ImsFeature {
*
* @hide
*/
+ @FlaggedApi(FLAG_SUPPORT_IMS_MMTEL_INTERFACE)
+ @SystemApi
public final void stopImsTrafficSession(@NonNull ImsTrafficSessionCallback callback) {
IImsMmTelListener listener = getListener();
if (listener == null) {
diff --git a/telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl b/telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl
index b4d93fddd7aa..974cc14ae444 100644
--- a/telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl
+++ b/telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl
@@ -208,10 +208,9 @@ interface IPhoneSubInfo {
/**
* Fetches the ISIM public user identities (EF_IMPU) from UICC based on subId
*/
- @JavaPassthrough(annotation="@android.annotation.RequiresPermission(" +
- "anyOf={android.Manifest.permission.READ_PHONE_NUMBERS, android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE})")
- List<Uri> getImsPublicUserIdentities(int subId, String callingPackage,
- String callingFeatureId);
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission("
+ + "android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)")
+ List<Uri> getImsPublicUserIdentities(int subId, String callingPackage);
/**
* Returns the IMS Service Table (IST) that was loaded from the ISIM.
@@ -227,6 +226,20 @@ interface IPhoneSubInfo {
String[] getIsimPcscf(int subId);
/**
+ * Fetches IMS Proxy Call Session Control Function(P-CSCF) based on the subscription.
+ *
+ * @param subId subscriptionId
+ * @param callingPackage package name of the caller
+ * @return List of IMS Proxy Call Session Control Function strings.
+ * @throws IllegalArgumentException if the subscriptionId is not valid
+ * @throws IllegalStateException in case the ISIM hasn’t been loaded.
+ * @throws SecurityException if the caller does not have the required permission
+ */
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission("
+ + "android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)")
+ List<String> getImsPcscfAddresses(int subId, String callingPackage);
+
+ /**
* Returns the response of the SIM application on the UICC to authentication
* challenge/response algorithm. The data string and challenge response are
* Base64 encoded Strings.
diff --git a/test-mock/src/android/test/mock/MockContext.java b/test-mock/src/android/test/mock/MockContext.java
index 6bf7ff501217..3247d1fe5222 100644
--- a/test-mock/src/android/test/mock/MockContext.java
+++ b/test-mock/src/android/test/mock/MockContext.java
@@ -53,6 +53,7 @@ import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.util.List;
import java.util.concurrent.Executor;
/**
@@ -607,6 +608,12 @@ public class MockContext extends Context {
throw new UnsupportedOperationException();
}
+ /** @hide */
+ @Override
+ public List<IntentFilter> getRegisteredIntentFilters(BroadcastReceiver receiver) {
+ throw new UnsupportedOperationException();
+ }
+
@Override
public ComponentName startService(Intent service) {
throw new UnsupportedOperationException();
diff --git a/tests/Input/src/com/android/server/input/InputShellCommandTest.java b/tests/Input/src/com/android/server/input/InputShellCommandTest.java
index 11f46335f017..a236244546cb 100644
--- a/tests/Input/src/com/android/server/input/InputShellCommandTest.java
+++ b/tests/Input/src/com/android/server/input/InputShellCommandTest.java
@@ -133,6 +133,21 @@ public class InputShellCommandTest {
assertThat(mInputEventInjector.mInjectedEvents).isEmpty();
}
+ @Test
+ public void testSwipeCommandEventFrequency() {
+ int[] durations = {100, 300, 500};
+ for (int durationMillis: durations) {
+ mInputEventInjector.mInjectedEvents.clear();
+ runCommand(String.format("swipe 200 800 200 200 %d", durationMillis));
+
+ // Add 2 events for ACTION_DOWN and ACTION_UP.
+ final int maxEventNum =
+ (int) Math.ceil(InputShellCommand.SWIPE_EVENT_HZ_DEFAULT
+ * (float) durationMillis / 1000) + 2;
+ assertThat(mInputEventInjector.mInjectedEvents.size()).isAtMost(maxEventNum);
+ }
+ }
+
private InputEvent getSingleInjectedInputEvent() {
assertThat(mInputEventInjector.mInjectedEvents).hasSize(1);
return mInputEventInjector.mInjectedEvents.get(0);
diff --git a/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt b/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt
index 19dea0c5e74a..0b147d63ed08 100644
--- a/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt
+++ b/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt
@@ -22,8 +22,10 @@ import android.content.pm.PackageManager
import android.content.res.Resources
import android.hardware.input.IInputManager
import android.hardware.input.AidlKeyGestureEvent
+import android.hardware.input.AppLaunchData
import android.hardware.input.IKeyGestureEventListener
import android.hardware.input.IKeyGestureHandler
+import android.hardware.input.InputGestureData
import android.hardware.input.InputManager
import android.hardware.input.InputManagerGlobal
import android.hardware.input.KeyGestureEvent
@@ -232,7 +234,7 @@ class KeyGestureControllerTests {
keyGestureController.handleKeyGesture(/* deviceId = */ 0, intArrayOf(KeyEvent.KEYCODE_HOME),
/* modifierState = */ 0, KeyGestureEvent.KEY_GESTURE_TYPE_HOME,
KeyGestureEvent.ACTION_GESTURE_COMPLETE, /* displayId */ 0,
- /* focusedToken = */ null, /* flags = */ 0
+ /* focusedToken = */ null, /* flags = */ 0, /* appLaunchData = */null
)
assertEquals(
@@ -259,6 +261,7 @@ class KeyGestureControllerTests {
val expectedKeys: IntArray,
val expectedModifierState: Int,
val expectedActions: IntArray,
+ val expectedAppLaunchData: AppLaunchData? = null,
) {
override fun toString(): String = name
}
@@ -884,6 +887,86 @@ class KeyGestureControllerTests {
}
@Test
+ @EnableFlags(com.android.window.flags.Flags.FLAG_ENABLE_TASK_RESIZING_KEYBOARD_SHORTCUTS)
+ fun testSnapLeftFreeformTask() {
+ val keyGestureController = KeyGestureController(context, testLooper.looper)
+ testKeyGestureInternal(
+ keyGestureController,
+ TestData(
+ "ALT + [ -> Resizes a task to fit the left half of the screen",
+ intArrayOf(
+ KeyEvent.KEYCODE_ALT_LEFT,
+ KeyEvent.KEYCODE_LEFT_BRACKET
+ ),
+ KeyGestureEvent.KEY_GESTURE_TYPE_SNAP_LEFT_FREEFORM_WINDOW,
+ intArrayOf(KeyEvent.KEYCODE_LEFT_BRACKET),
+ KeyEvent.META_ALT_ON,
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+ )
+ )
+ }
+
+ @Test
+ @EnableFlags(com.android.window.flags.Flags.FLAG_ENABLE_TASK_RESIZING_KEYBOARD_SHORTCUTS)
+ fun testSnapRightFreeformTask() {
+ val keyGestureController = KeyGestureController(context, testLooper.looper)
+ testKeyGestureInternal(
+ keyGestureController,
+ TestData(
+ "ALT + ] -> Resizes a task to fit the right half of the screen",
+ intArrayOf(
+ KeyEvent.KEYCODE_ALT_LEFT,
+ KeyEvent.KEYCODE_RIGHT_BRACKET
+ ),
+ KeyGestureEvent.KEY_GESTURE_TYPE_SNAP_RIGHT_FREEFORM_WINDOW,
+ intArrayOf(KeyEvent.KEYCODE_RIGHT_BRACKET),
+ KeyEvent.META_ALT_ON,
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+ )
+ )
+ }
+
+ @Test
+ @EnableFlags(com.android.window.flags.Flags.FLAG_ENABLE_TASK_RESIZING_KEYBOARD_SHORTCUTS)
+ fun testMaximizeFreeformTask() {
+ val keyGestureController = KeyGestureController(context, testLooper.looper)
+ testKeyGestureInternal(
+ keyGestureController,
+ TestData(
+ "ALT + '=' -> Maximizes a task to fit the screen",
+ intArrayOf(
+ KeyEvent.KEYCODE_ALT_LEFT,
+ KeyEvent.KEYCODE_EQUALS
+ ),
+ KeyGestureEvent.KEY_GESTURE_TYPE_MAXIMIZE_FREEFORM_WINDOW,
+ intArrayOf(KeyEvent.KEYCODE_EQUALS),
+ KeyEvent.META_ALT_ON,
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+ )
+ )
+ }
+
+ @Test
+ @EnableFlags(com.android.window.flags.Flags.FLAG_ENABLE_TASK_RESIZING_KEYBOARD_SHORTCUTS)
+ fun testRestoreFreeformTask() {
+ val keyGestureController = KeyGestureController(context, testLooper.looper)
+ testKeyGestureInternal(
+ keyGestureController,
+ TestData(
+ "ALT + '-' -> Restores a task size to its previous bounds",
+ intArrayOf(
+ KeyEvent.KEYCODE_ALT_LEFT,
+ KeyEvent.KEYCODE_MINUS
+ ),
+ KeyGestureEvent.KEY_GESTURE_TYPE_RESTORE_FREEFORM_WINDOW_SIZE,
+ intArrayOf(KeyEvent.KEYCODE_MINUS),
+ KeyEvent.META_ALT_ON,
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+ )
+ )
+ }
+
+ @Test
fun testCapsLockPressNotified() {
val keyGestureController = KeyGestureController(context, testLooper.looper)
val listener = KeyGestureEventListener()
@@ -975,6 +1058,62 @@ class KeyGestureControllerTests {
testKeyGestureInternal(keyGestureController, test)
}
+ @Keep
+ private fun customInputGesturesTestArguments(): Array<TestData> {
+ return arrayOf(
+ TestData(
+ "META + ALT + Q -> Go Home",
+ intArrayOf(
+ KeyEvent.KEYCODE_META_LEFT,
+ KeyEvent.KEYCODE_ALT_LEFT,
+ KeyEvent.KEYCODE_Q
+ ),
+ KeyGestureEvent.KEY_GESTURE_TYPE_HOME,
+ intArrayOf(KeyEvent.KEYCODE_Q),
+ KeyEvent.META_META_ON or KeyEvent.META_ALT_ON,
+ intArrayOf(
+ KeyGestureEvent.ACTION_GESTURE_COMPLETE
+ )
+ ),
+ TestData(
+ "META + ALT + Q -> Launch app",
+ intArrayOf(
+ KeyEvent.KEYCODE_CTRL_LEFT,
+ KeyEvent.KEYCODE_SHIFT_LEFT,
+ KeyEvent.KEYCODE_Q
+ ),
+ KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION,
+ intArrayOf(KeyEvent.KEYCODE_Q),
+ KeyEvent.META_CTRL_ON or KeyEvent.META_SHIFT_ON,
+ intArrayOf(
+ KeyGestureEvent.ACTION_GESTURE_COMPLETE
+ ),
+ AppLaunchData.createLaunchDataForComponent("com.test", "com.test.BookmarkTest")
+ ),
+ )
+ }
+
+ @Test
+ @Parameters(method = "customInputGesturesTestArguments")
+ fun testCustomKeyGestures(test: TestData) {
+ val keyGestureController = KeyGestureController(context, testLooper.looper)
+ 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.addCustomInputGesture(0, inputGestureData.aidlData)
+ testKeyGestureInternal(keyGestureController, test)
+ }
+
private fun testKeyGestureInternal(keyGestureController: KeyGestureController, test: TestData) {
var handleEvents = mutableListOf<KeyGestureEvent>()
val handler = KeyGestureHandler { event, _ ->
@@ -1013,6 +1152,11 @@ class KeyGestureControllerTests {
test.expectedActions[i],
event.action
)
+ assertEquals(
+ "Test: $test doesn't produce correct app launch data",
+ test.expectedAppLaunchData,
+ event.appLaunchData
+ )
}
keyGestureController.unregisterKeyGestureHandler(handler, 0)
diff --git a/tests/graphics/HwAccelerationTest/AndroidManifest.xml b/tests/graphics/HwAccelerationTest/AndroidManifest.xml
index db3a992b9c7b..05b2f4c53b15 100644
--- a/tests/graphics/HwAccelerationTest/AndroidManifest.xml
+++ b/tests/graphics/HwAccelerationTest/AndroidManifest.xml
@@ -24,7 +24,7 @@
<uses-feature android:name="android.hardware.camera"/>
<uses-feature android:name="android.hardware.camera.autofocus"/>
- <uses-sdk android:minSdkVersion="21"/>
+ <uses-sdk android:minSdkVersion="21" />
<application android:label="HwUi"
android:theme="@android:style/Theme.Material.Light">
@@ -409,6 +409,24 @@
</intent-filter>
</activity>
+ <activity android:name="ScrollingZAboveSurfaceView"
+ android:label="SurfaceView/Z-Above scrolling"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="com.android.test.hwui.TEST"/>
+ </intent-filter>
+ </activity>
+
+ <activity android:name="ScrollingZAboveScaledSurfaceView"
+ android:label="SurfaceView/Z-Above scrolling, scaled surface"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="com.android.test.hwui.TEST"/>
+ </intent-filter>
+ </activity>
+
<activity android:name="StretchySurfaceViewActivity"
android:label="SurfaceView/Stretchy Movement"
android:exported="true">
diff --git a/tests/graphics/HwAccelerationTest/res/layout/scrolling_zabove_surfaceview.xml b/tests/graphics/HwAccelerationTest/res/layout/scrolling_zabove_surfaceview.xml
new file mode 100644
index 000000000000..31e5774dd1ad
--- /dev/null
+++ b/tests/graphics/HwAccelerationTest/res/layout/scrolling_zabove_surfaceview.xml
@@ -0,0 +1,131 @@
+<?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.
+ -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ tools:context=".MainActivity">
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="Above the ScrollView"
+ android:textColor="#FFFFFFFF"
+ android:background="#FF444444"
+ android:padding="32dp" />
+
+ <ScrollView
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="Scrolling Header"
+ android:background="#FFCCCCCC"
+ android:padding="32dp" />
+
+ <SurfaceView
+ android:layout_width="match_parent"
+ android:layout_height="500dp"
+ android:id="@+id/surfaceview" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="Scrolling Item"
+ android:background="#FFCCCCCC"
+ android:padding="32dp" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="Scrolling Item"
+ android:background="#FFCCCCCC"
+ android:padding="32dp" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="Scrolling Item"
+ android:background="#FFCCCCCC"
+ android:padding="32dp" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="Scrolling Item"
+ android:background="#FFCCCCCC"
+ android:padding="32dp" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="Scrolling Item"
+ android:background="#FFCCCCCC"
+ android:padding="32dp" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="Scrolling Item"
+ android:background="#FFCCCCCC"
+ android:padding="32dp" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="Scrolling Item"
+ android:background="#FFCCCCCC"
+ android:padding="32dp" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="Scrolling Item"
+ android:background="#FFCCCCCC"
+ android:padding="32dp" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="Scrolling Item"
+ android:background="#FFCCCCCC"
+ android:padding="32dp" />
+
+ </LinearLayout>
+
+ </ScrollView>
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="Below the ScrollView"
+ android:textColor="#FFFFFFFF"
+ android:background="#FF444444"
+ android:padding="32dp" />
+
+</LinearLayout> \ No newline at end of file
diff --git a/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/ScrollingZAboveScaledSurfaceView.kt b/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/ScrollingZAboveScaledSurfaceView.kt
new file mode 100644
index 000000000000..59ae885664db
--- /dev/null
+++ b/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/ScrollingZAboveScaledSurfaceView.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.test.hwui
+
+import android.app.Activity
+import android.graphics.Color
+import android.graphics.Paint
+import android.os.Bundle
+import android.view.SurfaceHolder
+import android.view.SurfaceView
+
+class ScrollingZAboveScaledSurfaceView : Activity() {
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(R.layout.scrolling_zabove_surfaceview)
+
+ findViewById<SurfaceView>(R.id.surfaceview).apply {
+ setZOrderOnTop(true)
+ holder.setFixedSize(1000, 2000)
+ holder.addCallback(object : SurfaceHolder.Callback {
+ override fun surfaceCreated(p0: SurfaceHolder) {
+
+ }
+
+ override fun surfaceChanged(holder: SurfaceHolder, format: Int, width: Int, height: Int) {
+ holder.unlockCanvasAndPost(holder.lockCanvas().apply {
+ drawColor(Color.BLUE)
+ val paint = Paint()
+ paint.textSize = 16 * resources.displayMetrics.density
+ paint.textAlign = Paint.Align.CENTER
+ paint.color = Color.WHITE
+ drawText("I'm a setZOrderOnTop(true) SurfaceView!",
+ (width / 2).toFloat(), (height / 2).toFloat(), paint)
+ })
+ }
+
+ override fun surfaceDestroyed(p0: SurfaceHolder) {
+
+ }
+
+ })
+ }
+ }
+} \ No newline at end of file
diff --git a/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/ScrollingZAboveSurfaceView.kt b/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/ScrollingZAboveSurfaceView.kt
new file mode 100644
index 000000000000..ccb71ec0ff2a
--- /dev/null
+++ b/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/ScrollingZAboveSurfaceView.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.test.hwui
+
+import android.app.Activity
+import android.graphics.Color
+import android.graphics.Paint
+import android.os.Bundle
+import android.view.SurfaceHolder
+import android.view.SurfaceView
+
+class ScrollingZAboveSurfaceView : Activity() {
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(R.layout.scrolling_zabove_surfaceview)
+
+ findViewById<SurfaceView>(R.id.surfaceview).apply {
+ setZOrderOnTop(true)
+ holder.addCallback(object : SurfaceHolder.Callback {
+ override fun surfaceCreated(p0: SurfaceHolder) {
+
+ }
+
+ override fun surfaceChanged(holder: SurfaceHolder, format: Int, width: Int, height: Int) {
+ holder.unlockCanvasAndPost(holder.lockCanvas().apply {
+ drawColor(Color.BLUE)
+ val paint = Paint()
+ paint.textSize = 16 * resources.displayMetrics.density
+ paint.textAlign = Paint.Align.CENTER
+ paint.color = Color.WHITE
+ drawText("I'm a setZOrderOnTop(true) SurfaceView!",
+ (width / 2).toFloat(), (height / 2).toFloat(), paint)
+ })
+ }
+
+ override fun surfaceDestroyed(p0: SurfaceHolder) {
+
+ }
+
+ })
+ }
+ }
+} \ No newline at end of file