summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--PERFORMANCE_OWNERS1
-rw-r--r--ZYGOTE_OWNERS2
-rw-r--r--api/Android.bp7
-rw-r--r--api/StubLibraries.bp259
-rw-r--r--api/api.go81
-rw-r--r--api/api_test.go111
-rw-r--r--core/java/android/app/DisabledWallpaperManager.java185
-rw-r--r--core/java/android/app/WallpaperManager.java2
-rw-r--r--core/java/android/hardware/camera2/impl/CameraDeviceImpl.java2
-rw-r--r--core/java/android/os/vibrator/flags.aconfig18
-rw-r--r--core/java/android/view/DisplayInfo.java4
-rw-r--r--core/java/com/android/internal/accessibility/common/MagnificationConstants.java4
-rw-r--r--core/res/res/layout/input_method_switch_dialog_new.xml31
-rw-r--r--core/res/res/values/symbols.xml2
-rw-r--r--core/res/res/xml/haptic_feedback_customization.xml18
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java155
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java213
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenConstants.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java6
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java55
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java16
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragResizeWindowGeometryTests.java2
-rw-r--r--media/jni/JetPlayer.h8
-rw-r--r--native/android/surface_control_input_receiver.cpp2
-rw-r--r--packages/SettingsLib/SelectorWithWidgetPreference/Android.bp22
-rw-r--r--packages/SettingsLib/SelectorWithWidgetPreference/aconfig/selectorwithwidgetpreference.aconfig13
-rw-r--r--packages/SettingsLib/SelectorWithWidgetPreference/res/values/attrs.xml22
-rw-r--r--packages/SettingsLib/SelectorWithWidgetPreference/src/com/android/settingslib/widget/SelectorWithWidgetPreference.java38
-rw-r--r--packages/SettingsLib/tests/robotests/Android.bp1
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/SelectorWithWidgetPreferenceTest.java67
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/device_config_service.aconfig7
-rw-r--r--packages/SystemUI/aconfig/systemui.aconfig12
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/NotificationSection.kt9
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/AccessibilityQsShortcutsRepositoryImplTest.kt1
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/ColorCorrectionRepositoryImplTest.kt1
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/ColorInversionRepositoryImplTest.kt1
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/OneHandedModeRepositoryImplTest.kt1
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/UserA11yQsShortcutsRepositoryTest.kt1
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/camera/data/repository/CameraAutoRotateRepositoryImplTest.kt1
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalTutorialRepositoryImplTest.kt1
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/EditWidgetsActivityControllerTest.kt107
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/education/data/repository/ContextualEducationRepositoryTest.kt35
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractorTest.kt11
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/domain/interactor/IconTilesInteractorTest.kt10
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/compose/DragAndDropStateTest.kt39
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/compose/EditTileListStateTest.kt32
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/compose/PaginatableGridLayoutTest.kt8
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/AutoAddableSettingTest.kt2
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/NotificationShadeWindowModelTest.kt23
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt2
-rw-r--r--packages/SystemUI/res/layout/chipbar.xml4
-rw-r--r--packages/SystemUI/res/values/colors.xml2
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt108
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/education/dagger/ContextualEducationModule.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/education/data/repository/ContextualEducationRepository.kt66
-rw-r--r--packages/SystemUI/src/com/android/systemui/education/data/repository/UserContextualEducationRepository.kt39
-rw-r--r--packages/SystemUI/src/com/android/systemui/education/domain/interactor/ContextualEducationInteractor.kt19
-rw-r--r--packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/KeyboardTouchpadTutorialCoreStartable.kt (renamed from packages/SystemUI/src/com/android/systemui/inputdevice/oobe/KeyboardTouchpadOobeTutorialCoreStartable.kt)13
-rw-r--r--packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/data/model/TutorialSchedulerInfo.kt (renamed from packages/SystemUI/src/com/android/systemui/inputdevice/oobe/data/model/OobeSchedulerInfo.kt)8
-rw-r--r--packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/data/repository/TutorialSchedulerRepository.kt83
-rw-r--r--packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/domain/interactor/TutorialSchedulerInteractor.kt (renamed from packages/SystemUI/src/com/android/systemui/inputdevice/oobe/domain/interactor/OobeSchedulerInteractor.kt)58
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultNotificationStackScrollLayoutSection.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/IconTilesInteractor.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/InfiniteGridConsistencyInteractor.kt12
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/shared/model/TileRow.kt35
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/DragAndDropState.kt51
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/EditTileListState.kt17
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/InfiniteGridLayout.kt33
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/QuickQuickSettings.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/Tile.kt112
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/ui/model/TileGridCell.kt52
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/IconTilesViewModel.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/QuickQuickSettingsViewModel.kt39
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java27
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/QuickSettingsControllerImpl.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/NotificationShadeWindowModel.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/ui/viewmodel/ScreenRecordChipViewModel.kt27
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/ColorsModel.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractor.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerLegacyTest.kt53
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerTest.kt49
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/screenrecord/ui/viewmodel/ScreenRecordChipViewModelTest.kt28
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java3
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/education/data/repository/ContextualEducationRepositoryKosmos.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/education/data/repository/FakeContextualEducationRepository.kt20
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/education/domain/interactor/ContextualEducationInteractorKosmos.kt4
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/NotificationsShadeWindowModelKosmos.kt8
-rw-r--r--services/core/java/com/android/server/am/SettingsToPropertiesMapper.java3
-rw-r--r--services/core/java/com/android/server/audio/TEST_MAPPING3
-rw-r--r--services/core/java/com/android/server/inputmethod/InputMethodManagerService.java33
-rw-r--r--services/core/java/com/android/server/inputmethod/InputMethodSubtypeSwitchingController.java6
-rw-r--r--services/core/java/com/android/server/inputmethod/UserData.java13
-rw-r--r--services/core/java/com/android/server/net/NetworkPolicyManagerService.java57
-rw-r--r--services/core/java/com/android/server/vibrator/HapticFeedbackCustomization.java71
-rw-r--r--services/core/java/com/android/server/vibrator/HapticFeedbackVibrationProvider.java7
-rw-r--r--services/core/java/com/android/server/vibrator/VibrationSettings.java4
-rw-r--r--services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java38
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java22
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationConnectionManagerTest.java2
-rw-r--r--services/tests/vibrator/Android.bp1
-rw-r--r--services/tests/vibrator/src/com/android/server/vibrator/HapticFeedbackCustomizationTest.java262
-rw-r--r--services/tests/vibrator/src/com/android/server/vibrator/HapticFeedbackVibrationProviderTest.java34
-rw-r--r--services/tests/vibrator/src/com/android/server/vibrator/VibrationSettingsTest.java5
-rw-r--r--services/tests/vibrator/utils/com/android/server/vibrator/FakeXmlResourceParser.java330
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/FrameRateSelectionPriorityTests.java15
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/RefreshRatePolicyTest.java21
-rw-r--r--telephony/java/android/service/euicc/EuiccService.java11
-rw-r--r--test-mock/Android.bp4
117 files changed, 2352 insertions, 1333 deletions
diff --git a/PERFORMANCE_OWNERS b/PERFORMANCE_OWNERS
index 48a020130445..02b0a1ec75e7 100644
--- a/PERFORMANCE_OWNERS
+++ b/PERFORMANCE_OWNERS
@@ -6,3 +6,4 @@ philipcuadra@google.com
shayba@google.com
jdduke@google.com
shombert@google.com
+kevinjeon@google.com
diff --git a/ZYGOTE_OWNERS b/ZYGOTE_OWNERS
index f6d15e03a892..6918c16840dd 100644
--- a/ZYGOTE_OWNERS
+++ b/ZYGOTE_OWNERS
@@ -1,4 +1,4 @@
chriswailes@google.com
+hboehm@google.com
maco@google.com
-narayan@google.com
ngeoffray@google.com
diff --git a/api/Android.bp b/api/Android.bp
index d931df165a8f..341be3d53844 100644
--- a/api/Android.bp
+++ b/api/Android.bp
@@ -284,7 +284,7 @@ packages_to_document = [
// These are libs from framework-internal-utils that are required (i.e. being referenced)
// from framework-non-updatable-sources. Add more here when there's a need.
// DO NOT add the entire framework-internal-utils. It might cause unnecessary circular
-// dependencies gets bigger.
+// dependencies when the list gets bigger.
android_non_updatable_stubs_libs = [
"android.hardware.cas-V1.2-java",
"android.hardware.health-V1.0-java-constants",
@@ -384,6 +384,11 @@ non_updatable_api_deps_on_modules = [
"sdk_system_current_android",
]
+java_defaults {
+ name: "module-classpath-java-defaults",
+ libs: non_updatable_api_deps_on_modules,
+}
+
// Defaults with module APIs in the classpath (mostly from prebuilts).
// Suitable for compiling android-non-updatable.
stubs_defaults {
diff --git a/api/StubLibraries.bp b/api/StubLibraries.bp
index 8dfddf0e13c8..d991da59f167 100644
--- a/api/StubLibraries.bp
+++ b/api/StubLibraries.bp
@@ -563,8 +563,12 @@ java_library {
java_defaults {
name: "android-non-updatable_from_text_defaults",
+ defaults: ["android-non-updatable-stubs-libs-defaults"],
static_libs: ["framework-res-package-jar"],
libs: ["stub-annotations"],
+ sdk_version: "none",
+ system_modules: "none",
+ previous_api: ":android.api.public.latest",
}
java_defaults {
@@ -582,10 +586,10 @@ java_api_library {
"api-stubs-docs-non-updatable.api.contribution",
],
defaults: ["android-non-updatable_everything_from_text_defaults"],
- full_api_surface_stub: "android_stubs_current.from-text",
// Use full Android API not just the non-updatable API as the latter is incomplete
// and can result in incorrect behavior.
previous_api: ":android.api.combined.public.latest",
+ libs: ["all-modules-public-stubs"],
}
java_api_library {
@@ -596,10 +600,10 @@ java_api_library {
"system-api-stubs-docs-non-updatable.api.contribution",
],
defaults: ["android-non-updatable_everything_from_text_defaults"],
- full_api_surface_stub: "android_system_stubs_current.from-text",
// Use full Android API not just the non-updatable API as the latter is incomplete
// and can result in incorrect behavior.
previous_api: ":android.api.combined.system.latest",
+ libs: ["all-modules-system-stubs"],
}
java_api_library {
@@ -611,10 +615,10 @@ java_api_library {
"test-api-stubs-docs-non-updatable.api.contribution",
],
defaults: ["android-non-updatable_everything_from_text_defaults"],
- full_api_surface_stub: "android_test_stubs_current.from-text",
// Use full Android API not just the non-updatable API as the latter is incomplete
// and can result in incorrect behavior.
previous_api: ":android.api.combined.test.latest",
+ libs: ["all-modules-system-stubs"],
}
java_api_library {
@@ -625,8 +629,10 @@ java_api_library {
"system-api-stubs-docs-non-updatable.api.contribution",
"module-lib-api-stubs-docs-non-updatable.api.contribution",
],
- defaults: ["android-non-updatable_everything_from_text_defaults"],
- full_api_surface_stub: "android_module_lib_stubs_current_full.from-text",
+ defaults: [
+ "module-classpath-java-defaults",
+ "android-non-updatable_everything_from_text_defaults",
+ ],
// Use full Android API not just the non-updatable API as the latter is incomplete
// and can result in incorrect behavior.
previous_api: ":android.api.combined.module-lib.latest",
@@ -644,14 +650,16 @@ java_api_library {
"test-api-stubs-docs-non-updatable.api.contribution",
"module-lib-api-stubs-docs-non-updatable.api.contribution",
],
- defaults: ["android-non-updatable_everything_from_text_defaults"],
- full_api_surface_stub: "android_test_module_lib_stubs_current.from-text",
+ defaults: [
+ "module-classpath-java-defaults",
+ "android-non-updatable_everything_from_text_defaults",
+ ],
// No need to specify previous_api as this is not used for compiling against.
-
// This module is only used for hiddenapi, and other modules should not
// depend on this module.
visibility: ["//visibility:private"],
+ libs: ["all-modules-system-stubs"],
}
java_defaults {
@@ -665,7 +673,7 @@ java_defaults {
}
java_library {
- name: "android_stubs_current.from-source",
+ name: "android_stubs_current",
static_libs: [
"all-modules-public-stubs",
"android-non-updatable.stubs",
@@ -675,7 +683,7 @@ java_library {
}
java_library {
- name: "android_stubs_current_exportable.from-source",
+ name: "android_stubs_current_exportable",
static_libs: [
"all-modules-public-stubs-exportable",
"android-non-updatable.stubs.exportable",
@@ -685,7 +693,7 @@ java_library {
}
java_library {
- name: "android_system_stubs_current.from-source",
+ name: "android_system_stubs_current",
static_libs: [
"all-modules-system-stubs",
"android-non-updatable.stubs.system",
@@ -698,7 +706,7 @@ java_library {
}
java_library {
- name: "android_system_stubs_current_exportable.from-source",
+ name: "android_system_stubs_current_exportable",
static_libs: [
"all-modules-system-stubs-exportable",
"android-non-updatable.stubs.exportable.system",
@@ -722,7 +730,7 @@ java_library {
}
java_library {
- name: "android_test_stubs_current.from-source",
+ name: "android_test_stubs_current",
static_libs: [
// Updatable modules do not have test APIs, but we want to include their SystemApis, like we
// include the SystemApi of framework-non-updatable-sources.
@@ -739,7 +747,7 @@ java_library {
}
java_library {
- name: "android_test_stubs_current_exportable.from-source",
+ name: "android_test_stubs_current_exportable",
static_libs: [
// Updatable modules do not have test APIs, but we want to include their SystemApis, like we
// include the SystemApi of framework-non-updatable-sources.
@@ -760,7 +768,7 @@ java_library {
// This module does not need to be copied to dist
java_library {
- name: "android_test_frameworks_core_stubs_current.from-source",
+ name: "android_test_frameworks_core_stubs_current",
static_libs: [
"all-updatable-modules-system-stubs",
"android-non-updatable.stubs.test",
@@ -772,7 +780,7 @@ java_library {
}
java_library {
- name: "android_module_lib_stubs_current.from-source",
+ name: "android_module_lib_stubs_current",
defaults: [
"android.jar_defaults",
],
@@ -785,7 +793,7 @@ java_library {
}
java_library {
- name: "android_module_lib_stubs_current_exportable.from-source",
+ name: "android_module_lib_stubs_current_exportable",
defaults: [
"android.jar_defaults",
"android_stubs_dists_default",
@@ -801,20 +809,20 @@ java_library {
}
java_library {
- name: "android_system_server_stubs_current.from-source",
+ name: "android_system_server_stubs_current",
defaults: [
"android.jar_defaults",
],
srcs: [":services-non-updatable-stubs"],
installable: false,
static_libs: [
- "android_module_lib_stubs_current.from-source",
+ "android_module_lib_stubs_current",
],
visibility: ["//frameworks/base/services"],
}
java_library {
- name: "android_system_server_stubs_current_exportable.from-source",
+ name: "android_system_server_stubs_current_exportable",
defaults: [
"android.jar_defaults",
"android_stubs_dists_default",
@@ -822,7 +830,7 @@ java_library {
srcs: [":services-non-updatable-stubs{.exportable}"],
installable: false,
static_libs: [
- "android_module_lib_stubs_current_exportable.from-source",
+ "android_module_lib_stubs_current_exportable",
],
dist: {
dir: "apistubs/android/system-server",
@@ -897,215 +905,6 @@ java_genrule {
},
}
-//
-// Java API defaults and libraries for single tree build
-//
-
-java_defaults {
- name: "stub-annotation-defaults",
- libs: [
- "stub-annotations",
- ],
- static_libs: [
- // stub annotations do not contribute to the API surfaces but are statically
- // linked in the stubs for API surfaces (see frameworks/base/StubLibraries.bp).
- // This is because annotation processors insist on loading the classes for any
- // annotations found, thus should exist inside android.jar.
- "private-stub-annotations-jar",
- ],
- is_stubs_module: true,
-}
-
-// Listing of API domains contribution and dependencies per API surfaces
-java_defaults {
- name: "android_test_stubs_current_contributions",
- api_surface: "test",
- api_contributions: [
- "framework-virtualization.stubs.source.test.api.contribution",
- "framework-location.stubs.source.test.api.contribution",
- ],
-}
-
-java_defaults {
- name: "android_test_frameworks_core_stubs_current_contributions",
- api_surface: "test",
- api_contributions: [
- "test-api-stubs-docs-non-updatable.api.contribution",
- ],
-}
-
-java_defaults {
- name: "android_module_lib_stubs_current_contributions",
- api_surface: "module-lib",
- api_contributions: [
- "api-stubs-docs-non-updatable.api.contribution",
- "system-api-stubs-docs-non-updatable.api.contribution",
- "module-lib-api-stubs-docs-non-updatable.api.contribution",
- "art.module.public.api.stubs.source.api.contribution",
- "art.module.public.api.stubs.source.system.api.contribution",
- "art.module.public.api.stubs.source.module_lib.api.contribution",
- "i18n.module.public.api.stubs.source.api.contribution",
- "i18n.module.public.api.stubs.source.system.api.contribution",
- "i18n.module.public.api.stubs.source.module_lib.api.contribution",
- ],
- previous_api: ":android.api.combined.module-lib.latest",
-}
-
-// Java API library definitions per API surface
-java_api_library {
- name: "android_stubs_current.from-text",
- api_surface: "public",
- defaults: [
- // This module is dynamically created at frameworks/base/api/api.go
- // instead of being written out, in order to minimize edits in the codebase
- // when there is a change in the list of modules.
- // that contributes to an api surface.
- "android_stubs_current_contributions",
- "stub-annotation-defaults",
- ],
- api_contributions: [
- "api-stubs-docs-non-updatable.api.contribution",
- ],
- visibility: ["//visibility:public"],
- enable_validation: false,
- stubs_type: "everything",
-}
-
-java_api_library {
- name: "android_system_stubs_current.from-text",
- api_surface: "system",
- defaults: [
- "android_stubs_current_contributions",
- "android_system_stubs_current_contributions",
- "stub-annotation-defaults",
- ],
- api_contributions: [
- "api-stubs-docs-non-updatable.api.contribution",
- "system-api-stubs-docs-non-updatable.api.contribution",
- ],
- visibility: ["//visibility:public"],
- enable_validation: false,
- stubs_type: "everything",
-}
-
-java_api_library {
- name: "android_test_stubs_current.from-text",
- api_surface: "test",
- defaults: [
- "android_stubs_current_contributions",
- "android_system_stubs_current_contributions",
- "android_test_stubs_current_contributions",
- "stub-annotation-defaults",
- ],
- api_contributions: [
- "api-stubs-docs-non-updatable.api.contribution",
- "system-api-stubs-docs-non-updatable.api.contribution",
- "test-api-stubs-docs-non-updatable.api.contribution",
- ],
- visibility: ["//visibility:public"],
- enable_validation: false,
- stubs_type: "everything",
-}
-
-java_api_library {
- name: "android_test_frameworks_core_stubs_current.from-text",
- api_surface: "test",
- defaults: [
- "android_stubs_current_contributions",
- "android_system_stubs_current_contributions",
- "android_test_frameworks_core_stubs_current_contributions",
- ],
- libs: [
- "stub-annotations",
- ],
- api_contributions: [
- "api-stubs-docs-non-updatable.api.contribution",
- "system-api-stubs-docs-non-updatable.api.contribution",
- ],
- enable_validation: false,
- stubs_type: "everything",
-}
-
-java_api_library {
- name: "android_module_lib_stubs_current_full.from-text",
- api_surface: "module-lib",
- defaults: [
- "android_stubs_current_contributions",
- "android_system_stubs_current_contributions",
- "android_module_lib_stubs_current_contributions_full",
- ],
- libs: [
- "stub-annotations",
- ],
- api_contributions: [
- "api-stubs-docs-non-updatable.api.contribution",
- "system-api-stubs-docs-non-updatable.api.contribution",
- "module-lib-api-stubs-docs-non-updatable.api.contribution",
- ],
- visibility: ["//visibility:public"],
- enable_validation: false,
- stubs_type: "everything",
-}
-
-java_api_library {
- name: "android_module_lib_stubs_current.from-text",
- api_surface: "module-lib",
- defaults: [
- "android_module_lib_stubs_current_contributions",
- ],
- libs: [
- "android_module_lib_stubs_current_full.from-text",
- "stub-annotations",
- ],
- visibility: ["//visibility:public"],
- enable_validation: false,
- stubs_type: "everything",
-}
-
-java_api_library {
- name: "android_test_module_lib_stubs_current.from-text",
- api_surface: "module-lib",
- defaults: [
- "android_stubs_current_contributions",
- "android_system_stubs_current_contributions",
- "android_test_stubs_current_contributions",
- "android_module_lib_stubs_current_contributions",
- ],
- libs: [
- "android_module_lib_stubs_current_full.from-text",
- "stub-annotations",
- ],
- api_contributions: [
- "test-api-stubs-docs-non-updatable.api.contribution",
- ],
-
- // This module is only used to build android-non-updatable.stubs.test_module_lib
- // and other modules should not depend on this module.
- visibility: [
- "//visibility:private",
- ],
- enable_validation: false,
- stubs_type: "everything",
-}
-
-java_api_library {
- name: "android_system_server_stubs_current.from-text",
- api_surface: "system-server",
- api_contributions: [
- "services-non-updatable-stubs.api.contribution",
- ],
- libs: [
- "android_module_lib_stubs_current.from-text",
- "stub-annotations",
- ],
- static_libs: [
- "android_module_lib_stubs_current.from-text",
- ],
- visibility: ["//visibility:public"],
- enable_validation: false,
- stubs_type: "everything",
-}
-
////////////////////////////////////////////////////////////////////////
// api-versions.xml generation, for public and system. This API database
// also contains the android.test.* APIs.
diff --git a/api/api.go b/api/api.go
index b6b1a7e44510..5b7f534443fb 100644
--- a/api/api.go
+++ b/api/api.go
@@ -15,9 +15,7 @@
package api
import (
- "fmt"
"sort"
- "strings"
"github.com/google/blueprint/proptools"
@@ -464,79 +462,6 @@ func createMergedTxts(ctx android.LoadHookContext, bootclasspath, system_server_
}
}
-func createApiContributionDefaults(ctx android.LoadHookContext, modules []string) {
- defaultsSdkKinds := []android.SdkKind{
- android.SdkPublic, android.SdkSystem, android.SdkModule,
- }
- for _, sdkKind := range defaultsSdkKinds {
- props := defaultsProps{}
- props.Name = proptools.StringPtr(
- sdkKind.DefaultJavaLibraryName() + "_contributions")
- if sdkKind == android.SdkModule {
- props.Name = proptools.StringPtr(
- sdkKind.DefaultJavaLibraryName() + "_contributions_full")
- }
- props.Api_surface = proptools.StringPtr(sdkKind.String())
- apiSuffix := ""
- if sdkKind != android.SdkPublic {
- apiSuffix = "." + strings.ReplaceAll(sdkKind.String(), "-", "_")
- }
- props.Api_contributions = transformArray(
- modules, "", fmt.Sprintf(".stubs.source%s.api.contribution", apiSuffix))
- props.Defaults_visibility = []string{"//visibility:public"}
- props.Previous_api = proptools.StringPtr(":android.api.combined." + sdkKind.String() + ".latest")
- ctx.CreateModule(java.DefaultsFactory, &props)
- }
-}
-
-func createFullApiLibraries(ctx android.LoadHookContext) {
- javaLibraryNames := []string{
- "android_stubs_current",
- "android_system_stubs_current",
- "android_test_stubs_current",
- "android_test_frameworks_core_stubs_current",
- "android_module_lib_stubs_current",
- "android_system_server_stubs_current",
- }
-
- for _, libraryName := range javaLibraryNames {
- props := libraryProps{}
- props.Name = proptools.StringPtr(libraryName)
- staticLib := libraryName + ".from-source"
- if ctx.Config().BuildFromTextStub() {
- staticLib = libraryName + ".from-text"
- }
- props.Static_libs = []string{staticLib}
- props.Defaults = []string{"android.jar_defaults"}
- props.Visibility = []string{"//visibility:public"}
- props.Is_stubs_module = proptools.BoolPtr(true)
-
- ctx.CreateModule(java.LibraryFactory, &props)
- }
-}
-
-func createFullExportableApiLibraries(ctx android.LoadHookContext) {
- javaLibraryNames := []string{
- "android_stubs_current_exportable",
- "android_system_stubs_current_exportable",
- "android_test_stubs_current_exportable",
- "android_module_lib_stubs_current_exportable",
- "android_system_server_stubs_current_exportable",
- }
-
- for _, libraryName := range javaLibraryNames {
- props := libraryProps{}
- props.Name = proptools.StringPtr(libraryName)
- staticLib := libraryName + ".from-source"
- props.Static_libs = []string{staticLib}
- props.Defaults = []string{"android.jar_defaults"}
- props.Visibility = []string{"//visibility:public"}
- props.Is_stubs_module = proptools.BoolPtr(true)
-
- ctx.CreateModule(java.LibraryFactory, &props)
- }
-}
-
func (a *CombinedApis) createInternalModules(ctx android.LoadHookContext) {
bootclasspath := a.bootclasspath(ctx)
system_server_classpath := a.systemServerClasspath(ctx)
@@ -562,12 +487,6 @@ func (a *CombinedApis) createInternalModules(ctx android.LoadHookContext) {
createMergedAnnotationsFilegroups(ctx, bootclasspath, system_server_classpath)
createPublicStubsSourceFilegroup(ctx, bootclasspath)
-
- createApiContributionDefaults(ctx, bootclasspath)
-
- createFullApiLibraries(ctx)
-
- createFullExportableApiLibraries(ctx)
}
func combinedApisModuleFactory() android.Module {
diff --git a/api/api_test.go b/api/api_test.go
index 47d167093b39..fb26f821eec1 100644
--- a/api/api_test.go
+++ b/api/api_test.go
@@ -52,6 +52,12 @@ func gatherRequiredDepsForTest() string {
"core.current.stubs",
"ext",
"framework",
+ "android_stubs_current",
+ "android_system_stubs_current",
+ "android_test_stubs_current",
+ "android_test_frameworks_core_stubs_current",
+ "android_module_lib_stubs_current",
+ "android_system_server_stubs_current",
"android_stubs_current.from-text",
"android_system_stubs_current.from-text",
"android_test_stubs_current.from-text",
@@ -190,61 +196,60 @@ func TestCombinedApisDefaults(t *testing.T) {
}
}),
).RunTestWithBp(t, `
- java_sdk_library {
- name: "framework-foo",
- srcs: ["a.java"],
- public: {
- enabled: true,
- },
- system: {
- enabled: true,
- },
- test: {
- enabled: true,
- },
- module_lib: {
- enabled: true,
- },
- api_packages: [
- "foo",
- ],
- sdk_version: "core_current",
- annotations_enabled: true,
- }
+ java_sdk_library {
+ name: "framework-foo",
+ srcs: ["a.java"],
+ public: {
+ enabled: true,
+ },
+ system: {
+ enabled: true,
+ },
+ test: {
+ enabled: true,
+ },
+ module_lib: {
+ enabled: true,
+ },
+ api_packages: [
+ "foo",
+ ],
+ sdk_version: "core_current",
+ annotations_enabled: true,
+ }
+ java_sdk_library {
+ name: "framework-bar",
+ srcs: ["a.java"],
+ public: {
+ enabled: true,
+ },
+ system: {
+ enabled: true,
+ },
+ test: {
+ enabled: true,
+ },
+ module_lib: {
+ enabled: true,
+ },
+ api_packages: [
+ "foo",
+ ],
+ sdk_version: "core_current",
+ annotations_enabled: true,
+ }
- java_sdk_library {
- name: "framework-bar",
- srcs: ["a.java"],
- public: {
- enabled: true,
- },
- system: {
- enabled: true,
- },
- test: {
- enabled: true,
- },
- module_lib: {
- enabled: true,
- },
- api_packages: [
- "foo",
+ combined_apis {
+ name: "foo",
+ bootclasspath: [
+ "framework-bar",
+ ] + select(boolean_var_for_testing(), {
+ true: [
+ "framework-foo",
],
- sdk_version: "core_current",
- annotations_enabled: true,
- }
-
- combined_apis {
- name: "foo",
- bootclasspath: [
- "framework-bar",
- ] + select(boolean_var_for_testing(), {
- true: [
- "framework-foo",
- ],
- default: [],
- }),
- }
+ default: [],
+ }),
+ }
`)
subModuleDependsOnSelectAppendedModule := java.CheckModuleHasDependency(t,
diff --git a/core/java/android/app/DisabledWallpaperManager.java b/core/java/android/app/DisabledWallpaperManager.java
index 4a5836cef76d..b06fb9e2f284 100644
--- a/core/java/android/app/DisabledWallpaperManager.java
+++ b/core/java/android/app/DisabledWallpaperManager.java
@@ -15,11 +15,16 @@
*/
package android.app;
+import android.annotation.FloatRange;
import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.ComponentName;
import android.content.Intent;
import android.graphics.Bitmap;
+import android.graphics.Point;
import android.graphics.Rect;
+import android.graphics.RectF;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Bundle;
@@ -27,9 +32,12 @@ import android.os.Handler;
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
import android.util.Log;
+import android.util.SparseArray;
import java.io.IOException;
import java.io.InputStream;
+import java.util.List;
+import java.util.Map;
/**
* A no-op implementation of {@link WallpaperManager}.
@@ -54,29 +62,19 @@ final class DisabledWallpaperManager extends WallpaperManager {
private DisabledWallpaperManager() {
}
- @Override
- public boolean isWallpaperSupported() {
- return false;
+ @UnsupportedAppUsage
+ public IWallpaperManager getIWallpaperManager() {
+ return unsupported();
}
@Override
- public boolean isSetWallpaperAllowed() {
- return false;
- }
-
- private static <T> T unsupported() {
- if (DEBUG) Log.w(TAG, "unsupported method called; returning null", new Exception());
- return null;
- }
-
- private static boolean unsupportedBoolean() {
- if (DEBUG) Log.w(TAG, "unsupported method called; returning false", new Exception());
- return false;
+ public boolean isLockscreenLiveWallpaperEnabled() {
+ return unsupportedBoolean();
}
- private static int unsupportedInt() {
- if (DEBUG) Log.w(TAG, "unsupported method called; returning -1", new Exception());
- return -1;
+ @Override
+ public boolean shouldEnableWideColorGamut() {
+ return unsupportedBoolean();
}
@Override
@@ -122,6 +120,11 @@ final class DisabledWallpaperManager extends WallpaperManager {
}
@Override
+ public boolean wallpaperSupportsWcg(int which) {
+ return unsupportedBoolean();
+ }
+
+ @Override
public Bitmap getBitmap() {
return unsupported();
}
@@ -131,12 +134,61 @@ final class DisabledWallpaperManager extends WallpaperManager {
return unsupported();
}
+ @Nullable
+ public Bitmap getBitmap(boolean hardware, @SetWallpaperFlags int which) {
+ return unsupported();
+ }
+
@Override
public Bitmap getBitmapAsUser(int userId, boolean hardware) {
return unsupported();
}
@Override
+ public Bitmap getBitmapAsUser(int userId, boolean hardware, @SetWallpaperFlags int which) {
+ return unsupported();
+ }
+
+ @Override
+ public Bitmap getBitmapAsUser(int userId, boolean hardware,
+ @SetWallpaperFlags int which, boolean returnDefault) {
+ return unsupported();
+ }
+
+ @Override
+ public Rect peekBitmapDimensions() {
+ return unsupported();
+ }
+
+ @Override
+ public Rect peekBitmapDimensions(@SetWallpaperFlags int which) {
+ return unsupported();
+ }
+
+ @Nullable
+ public Rect peekBitmapDimensions(@SetWallpaperFlags int which, boolean returnDefault) {
+ return unsupported();
+ }
+
+ @Override
+ public List<Rect> getBitmapCrops(@NonNull List<Point> displaySizes,
+ @SetWallpaperFlags int which, boolean originalBitmap) {
+ return unsupported();
+ }
+
+ @Override
+ public List<Rect> getBitmapCrops(@NonNull Point bitmapSize, @NonNull List<Point> displaySizes,
+ @Nullable Map<Point, Rect> cropHints) {
+ return unsupported();
+ }
+
+ @Override
+ public WallpaperColors getWallpaperColors(@NonNull Bitmap bitmap,
+ @Nullable Map<Point, Rect> cropHints) {
+ return unsupported();
+ }
+
+ @Override
public ParcelFileDescriptor getWallpaperFile(int which) {
return unsupported();
}
@@ -173,6 +225,17 @@ final class DisabledWallpaperManager extends WallpaperManager {
}
@Override
+ public void addOnColorsChangedListener(@NonNull LocalWallpaperColorConsumer callback,
+ List<RectF> regions, int which) throws IllegalArgumentException {
+ unsupported();
+ }
+
+ @Override
+ public void removeOnColorsChangedListener(@NonNull LocalWallpaperColorConsumer callback) {
+ unsupported();
+ }
+
+ @Override
public ParcelFileDescriptor getWallpaperFile(int which, int userId) {
return unsupported();
}
@@ -192,23 +255,22 @@ final class DisabledWallpaperManager extends WallpaperManager {
return unsupported();
}
- @Override
- public ParcelFileDescriptor getWallpaperInfoFile() {
+ public WallpaperInfo getWallpaperInfoForUser(int userId) {
return unsupported();
}
@Override
- public WallpaperInfo getWallpaperInfoForUser(int userId) {
+ public WallpaperInfo getWallpaperInfo(@SetWallpaperFlags int which) {
return unsupported();
}
@Override
- public WallpaperInfo getWallpaperInfo(@SetWallpaperFlags int which) {
+ public WallpaperInfo getWallpaperInfo(@SetWallpaperFlags int which, int userId) {
return unsupported();
}
@Override
- public WallpaperInfo getWallpaperInfo(@SetWallpaperFlags int which, int userId) {
+ public ParcelFileDescriptor getWallpaperInfoFile() {
return unsupported();
}
@@ -264,6 +326,11 @@ final class DisabledWallpaperManager extends WallpaperManager {
return 0;
}
+ public int setBitmapWithCrops(@Nullable Bitmap fullImage, @NonNull Map<Point, Rect> cropHints,
+ boolean allowBackup, @SetWallpaperFlags int which) throws IOException {
+ return unsupportedInt();
+ }
+
@Override
public void setStream(InputStream bitmapData) throws IOException {
unsupported();
@@ -284,6 +351,19 @@ final class DisabledWallpaperManager extends WallpaperManager {
}
@Override
+ public int setStreamWithCrops(InputStream bitmapData, @NonNull Map<Point, Rect> cropHints,
+ boolean allowBackup, @SetWallpaperFlags int which) throws IOException {
+ return unsupportedInt();
+ }
+
+
+ @Override
+ public int setStreamWithCrops(InputStream bitmapData, @NonNull SparseArray<Rect> cropHints,
+ boolean allowBackup, @SetWallpaperFlags int which) throws IOException {
+ return unsupportedInt();
+ }
+
+ @Override
public boolean hasResourceWallpaper(int resid) {
return unsupportedBoolean();
}
@@ -328,12 +408,40 @@ final class DisabledWallpaperManager extends WallpaperManager {
return unsupportedBoolean();
}
+
+ @Override
+ public void setWallpaperDimAmount(@FloatRange(from = 0f, to = 1f) float dimAmount) {
+ unsupported();
+ }
+
+ @Override
+ public @FloatRange(from = 0f, to = 1f) float getWallpaperDimAmount() {
+ return unsupportedInt();
+ }
+
+ @Override
+ public boolean lockScreenWallpaperExists() {
+ return unsupportedBoolean();
+ }
+
@Override
public boolean setWallpaperComponent(ComponentName name, int userId) {
return unsupportedBoolean();
}
@Override
+ public boolean setWallpaperComponentWithFlags(@NonNull ComponentName name,
+ @SetWallpaperFlags int which) {
+ return unsupportedBoolean();
+ }
+
+ @Override
+ public boolean setWallpaperComponentWithFlags(@NonNull ComponentName name,
+ @SetWallpaperFlags int which, int userId) {
+ return unsupportedBoolean();
+ }
+
+ @Override
public void setWallpaperOffsets(IBinder windowToken, float xOffset, float yOffset) {
unsupported();
}
@@ -350,6 +458,21 @@ final class DisabledWallpaperManager extends WallpaperManager {
}
@Override
+ public void setWallpaperZoomOut(@NonNull IBinder windowToken, float zoom) {
+ unsupported();
+ }
+
+ @Override
+ public boolean isWallpaperSupported() {
+ return false;
+ }
+
+ @Override
+ public boolean isSetWallpaperAllowed() {
+ return false;
+ }
+
+ @Override
public void clearWallpaperOffsets(IBinder windowToken) {
unsupported();
}
@@ -369,8 +492,18 @@ final class DisabledWallpaperManager extends WallpaperManager {
return unsupportedBoolean();
}
- @Override
- public boolean wallpaperSupportsWcg(int which) {
- return unsupportedBoolean();
+ private static <T> T unsupported() {
+ if (DEBUG) Log.w(TAG, "unsupported method called; returning null", new Exception());
+ return null;
+ }
+
+ private static boolean unsupportedBoolean() {
+ if (DEBUG) Log.w(TAG, "unsupported method called; returning false", new Exception());
+ return false;
+ }
+
+ private static int unsupportedInt() {
+ if (DEBUG) Log.w(TAG, "unsupported method called; returning -1", new Exception());
+ return -1;
}
}
diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java
index 1a72df10fbd6..5903a7ff619c 100644
--- a/core/java/android/app/WallpaperManager.java
+++ b/core/java/android/app/WallpaperManager.java
@@ -123,6 +123,8 @@ import java.util.concurrent.TimeUnit;
* <p> An app can check whether wallpapers are supported for the current user, by calling
* {@link #isWallpaperSupported()}, and whether setting of wallpapers is allowed, by calling
* {@link #isSetWallpaperAllowed()}.
+ * Any public APIs added to WallpaperManager should have a corresponding stub in
+ * {@link DisabledWallpaperManager}.
*/
@SystemService(Context.WALLPAPER_SERVICE)
public class WallpaperManager {
diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
index 7f3c49dbb580..1e7f70bf5a72 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -918,7 +918,7 @@ public class CameraDeviceImpl extends CameraDevice
checkIfCameraClosedOrInError();
for (String physicalId : physicalCameraIdSet) {
- if (physicalId == getId()) {
+ if (Objects.equals(physicalId, getId())) {
throw new IllegalStateException("Physical id matches the logical id!");
}
}
diff --git a/core/java/android/os/vibrator/flags.aconfig b/core/java/android/os/vibrator/flags.aconfig
index 40dd91fcb4ae..0ed9b0316e4a 100644
--- a/core/java/android/os/vibrator/flags.aconfig
+++ b/core/java/android/os/vibrator/flags.aconfig
@@ -20,13 +20,6 @@ flag {
flag {
namespace: "haptics"
- name: "keyboard_category_enabled"
- description: "Enables the independent keyboard vibration settings feature"
- bug: "289107579"
-}
-
-flag {
- namespace: "haptics"
name: "adaptive_haptics_enabled"
description: "Enables the adaptive haptics feature"
bug: "305961689"
@@ -95,3 +88,14 @@ flag {
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ namespace: "haptics"
+ name: "load_haptic_feedback_vibration_customization_from_resources"
+ description: "Load haptic feedback vibrations customization from resources."
+ is_fixed_read_only: true
+ bug: "295142743"
+ metadata {
+ purpose: PURPOSE_FEATURE
+ }
+}
diff --git a/core/java/android/view/DisplayInfo.java b/core/java/android/view/DisplayInfo.java
index 12dbc5afd0a3..157cec8a4d0f 100644
--- a/core/java/android/view/DisplayInfo.java
+++ b/core/java/android/view/DisplayInfo.java
@@ -708,7 +708,7 @@ public final class DisplayInfo implements Parcelable {
*/
@Nullable
public Display.Mode findDefaultModeByRefreshRate(float refreshRate) {
- Display.Mode[] modes = supportedModes;
+ Display.Mode[] modes = appsSupportedModes;
Display.Mode defaultMode = getDefaultMode();
for (int i = 0; i < modes.length; i++) {
if (modes[i].matches(
@@ -723,7 +723,7 @@ public final class DisplayInfo implements Parcelable {
* Returns the list of supported refresh rates in the default mode.
*/
public float[] getDefaultRefreshRates() {
- Display.Mode[] modes = supportedModes;
+ Display.Mode[] modes = appsSupportedModes;
ArraySet<Float> rates = new ArraySet<>();
Display.Mode defaultMode = getDefaultMode();
for (int i = 0; i < modes.length; i++) {
diff --git a/core/java/com/android/internal/accessibility/common/MagnificationConstants.java b/core/java/com/android/internal/accessibility/common/MagnificationConstants.java
index 2db3e658530f..67e587e17e31 100644
--- a/core/java/com/android/internal/accessibility/common/MagnificationConstants.java
+++ b/core/java/com/android/internal/accessibility/common/MagnificationConstants.java
@@ -33,7 +33,7 @@ public final class MagnificationConstants {
/** Minimum supported value for magnification scale. */
public static final float SCALE_MIN_VALUE = 1.0f;
- /** Maximum supported value for magnification scale. Default of 8.0. */
+ /** Maximum supported value for magnification scale. Default of 20.0. */
public static final float SCALE_MAX_VALUE =
- Float.parseFloat(SystemProperties.get("ro.config.max_magnification_scale", "8.0"));
+ Float.parseFloat(SystemProperties.get("ro.config.max_magnification_scale", "20.0"));
}
diff --git a/core/res/res/layout/input_method_switch_dialog_new.xml b/core/res/res/layout/input_method_switch_dialog_new.xml
index 6bb969bf49ad..ab5d38f52f7d 100644
--- a/core/res/res/layout/input_method_switch_dialog_new.xml
+++ b/core/res/res/layout/input_method_switch_dialog_new.xml
@@ -21,21 +21,30 @@
android:layout_height="wrap_content"
android:orientation="vertical">
- <com.android.internal.widget.MaxHeightFrameLayout
- android:layout_width="320dp"
+ <LinearLayout
+ android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_weight="1"
- android:maxHeight="373dp">
+ android:orientation="horizontal">
- <com.android.internal.widget.RecyclerView
- android:id="@+id/list"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:paddingVertical="8dp"
- android:clipToPadding="false"
- android:layoutManager="com.android.internal.widget.LinearLayoutManager"/>
+ <!-- TODO(b/357644229): Enable shrinking width without three levels of nesting. -->
+ <com.android.internal.widget.MaxHeightFrameLayout
+ android:layout_width="320dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:maxHeight="373dp">
- </com.android.internal.widget.MaxHeightFrameLayout>
+ <com.android.internal.widget.RecyclerView
+ android:id="@+id/list"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:paddingVertical="8dp"
+ android:clipToPadding="false"
+ android:layoutManager="com.android.internal.widget.LinearLayoutManager"/>
+
+ </com.android.internal.widget.MaxHeightFrameLayout>
+
+ </LinearLayout>
<LinearLayout
style="?android:attr/buttonBarStyle"
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index bdcba9daa5ad..0d16e9c939d9 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -5462,7 +5462,9 @@
<java-symbol type="bool" name="config_enable_a11y_fullscreen_magnification_overscroll_handler" />
<java-symbol type="dimen" name="accessibility_fullscreen_magnification_gesture_edge_slop" />
+ <!-- For HapticFeedbackConstants configurability defined at HapticFeedbackCustomization -->
<java-symbol type="string" name="config_hapticFeedbackCustomizationFile" />
+ <java-symbol type="xml" name="haptic_feedback_customization" />
<!-- For ActivityManager PSS profiling configurability -->
<java-symbol type="bool" name="config_am_disablePssProfiling" />
diff --git a/core/res/res/xml/haptic_feedback_customization.xml b/core/res/res/xml/haptic_feedback_customization.xml
new file mode 100644
index 000000000000..7ac0787ab7a0
--- /dev/null
+++ b/core/res/res/xml/haptic_feedback_customization.xml
@@ -0,0 +1,18 @@
+<?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.
+ -->
+
+<haptic-feedback-constants/>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java
index 19a109e9a28c..e2988bc6f2aa 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java
@@ -23,7 +23,10 @@ import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMA
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
+import static com.android.wm.shell.common.split.SplitLayout.BEHIND_APP_VEIL_LAYER;
+import static com.android.wm.shell.common.split.SplitLayout.FRONT_APP_VEIL_LAYER;
import static com.android.wm.shell.common.split.SplitScreenConstants.FADE_DURATION;
+import static com.android.wm.shell.common.split.SplitScreenConstants.VEIL_DELAY_DURATION;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -74,7 +77,7 @@ public class SplitDecorManager extends WindowlessWindowManager {
private final SurfaceSession mSurfaceSession;
private Drawable mIcon;
- private ImageView mResizingIconView;
+ private ImageView mVeilIconView;
private SurfaceControlViewHost mViewHost;
private SurfaceControl mHostLeash;
private SurfaceControl mIconLeash;
@@ -83,13 +86,14 @@ public class SplitDecorManager extends WindowlessWindowManager {
private SurfaceControl mScreenshot;
private boolean mShown;
- private boolean mIsResizing;
+ /** True if the task is going through some kind of transition (moving or changing size). */
+ private boolean mIsCurrentlyChanging;
/** The original bounds of the main task, captured at the beginning of a resize transition. */
private final Rect mOldMainBounds = new Rect();
/** The original bounds of the side task, captured at the beginning of a resize transition. */
private final Rect mOldSideBounds = new Rect();
/** The current bounds of the main task, mid-resize. */
- private final Rect mResizingBounds = new Rect();
+ private final Rect mInstantaneousBounds = new Rect();
private final Rect mTempRect = new Rect();
private ValueAnimator mFadeAnimator;
private ValueAnimator mScreenshotAnimator;
@@ -134,7 +138,7 @@ public class SplitDecorManager extends WindowlessWindowManager {
mIconSize = context.getResources().getDimensionPixelSize(R.dimen.split_icon_size);
final FrameLayout rootLayout = (FrameLayout) LayoutInflater.from(context)
.inflate(R.layout.split_decor, null);
- mResizingIconView = rootLayout.findViewById(R.id.split_resizing_icon);
+ mVeilIconView = rootLayout.findViewById(R.id.split_resizing_icon);
final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
0 /* width */, 0 /* height */, TYPE_APPLICATION_OVERLAY,
@@ -191,28 +195,28 @@ public class SplitDecorManager extends WindowlessWindowManager {
}
mHostLeash = null;
mIcon = null;
- mResizingIconView = null;
- mIsResizing = false;
+ mVeilIconView = null;
+ mIsCurrentlyChanging = false;
mShown = false;
mOldMainBounds.setEmpty();
mOldSideBounds.setEmpty();
- mResizingBounds.setEmpty();
+ mInstantaneousBounds.setEmpty();
}
/** Showing resizing hint. */
public void onResizing(ActivityManager.RunningTaskInfo resizingTask, Rect newBounds,
Rect sideBounds, SurfaceControl.Transaction t, int offsetX, int offsetY,
boolean immediately) {
- if (mResizingIconView == null) {
+ if (mVeilIconView == null) {
return;
}
- if (!mIsResizing) {
- mIsResizing = true;
+ if (!mIsCurrentlyChanging) {
+ mIsCurrentlyChanging = true;
mOldMainBounds.set(newBounds);
mOldSideBounds.set(sideBounds);
}
- mResizingBounds.set(newBounds);
+ mInstantaneousBounds.set(newBounds);
mOffsetX = offsetX;
mOffsetY = offsetY;
@@ -254,8 +258,8 @@ public class SplitDecorManager extends WindowlessWindowManager {
if (mIcon == null && resizingTask.topActivityInfo != null) {
mIcon = mIconProvider.getIcon(resizingTask.topActivityInfo);
- mResizingIconView.setImageDrawable(mIcon);
- mResizingIconView.setVisibility(View.VISIBLE);
+ mVeilIconView.setImageDrawable(mIcon);
+ mVeilIconView.setVisibility(View.VISIBLE);
WindowManager.LayoutParams lp =
(WindowManager.LayoutParams) mViewHost.getView().getLayoutParams();
@@ -275,7 +279,12 @@ public class SplitDecorManager extends WindowlessWindowManager {
t.setAlpha(mIconLeash, showVeil ? 1f : 0f);
t.setVisibility(mIconLeash, showVeil);
} else {
- startFadeAnimation(showVeil, false, null);
+ startFadeAnimation(
+ showVeil,
+ false /* releaseSurface */,
+ null /* finishedCallback */,
+ false /* addDelay */
+ );
}
mShown = showVeil;
}
@@ -320,19 +329,19 @@ public class SplitDecorManager extends WindowlessWindowManager {
mScreenshotAnimator.start();
}
- if (mResizingIconView == null) {
+ if (mVeilIconView == null) {
if (mRunningAnimationCount == 0 && animFinishedCallback != null) {
animFinishedCallback.accept(false);
}
return;
}
- mIsResizing = false;
+ mIsCurrentlyChanging = false;
mOffsetX = 0;
mOffsetY = 0;
mOldMainBounds.setEmpty();
mOldSideBounds.setEmpty();
- mResizingBounds.setEmpty();
+ mInstantaneousBounds.setEmpty();
if (mFadeAnimator != null && mFadeAnimator.isRunning()) {
if (!mShown) {
// If fade-out animation is running, just add release callback to it.
@@ -356,7 +365,7 @@ public class SplitDecorManager extends WindowlessWindowManager {
if (mRunningAnimationCount == 0 && animFinishedCallback != null) {
animFinishedCallback.accept(true);
}
- });
+ }, false /* addDelay */);
} else {
// Decor surface is hidden so release it directly.
releaseDecor(t);
@@ -366,9 +375,94 @@ public class SplitDecorManager extends WindowlessWindowManager {
}
}
+ /**
+ * Called (on every frame) when two split apps are swapping, and a veil is needed.
+ */
+ public void drawNextVeilFrameForSwapAnimation(ActivityManager.RunningTaskInfo resizingTask,
+ Rect newBounds, SurfaceControl.Transaction t, boolean isGoingBehind,
+ SurfaceControl leash, float iconOffsetX, float iconOffsetY) {
+ if (mVeilIconView == null) {
+ return;
+ }
+
+ if (!mIsCurrentlyChanging) {
+ mIsCurrentlyChanging = true;
+ }
+
+ mInstantaneousBounds.set(newBounds);
+ mOffsetX = (int) iconOffsetX;
+ mOffsetY = (int) iconOffsetY;
+
+ t.setLayer(leash, isGoingBehind ? BEHIND_APP_VEIL_LAYER : FRONT_APP_VEIL_LAYER);
+
+ if (!mShown) {
+ if (mFadeAnimator != null && mFadeAnimator.isRunning()) {
+ // Cancel mFadeAnimator if it is running
+ mFadeAnimator.cancel();
+ }
+ }
+
+ if (mBackgroundLeash == null) {
+ // Initialize background
+ mBackgroundLeash = SurfaceUtils.makeColorLayer(mHostLeash,
+ RESIZING_BACKGROUND_SURFACE_NAME, mSurfaceSession);
+ t.setColor(mBackgroundLeash, getResizingBackgroundColor(resizingTask))
+ .setLayer(mBackgroundLeash, Integer.MAX_VALUE - 1);
+ }
+
+ if (mIcon == null && resizingTask.topActivityInfo != null) {
+ // Initialize icon
+ mIcon = mIconProvider.getIcon(resizingTask.topActivityInfo);
+ mVeilIconView.setImageDrawable(mIcon);
+ mVeilIconView.setVisibility(View.VISIBLE);
+
+ WindowManager.LayoutParams lp =
+ (WindowManager.LayoutParams) mViewHost.getView().getLayoutParams();
+ lp.width = mIconSize;
+ lp.height = mIconSize;
+ mViewHost.relayout(lp);
+
+ t.setLayer(mIconLeash, Integer.MAX_VALUE);
+ }
+
+ t.setPosition(mIconLeash,
+ newBounds.width() / 2 - mIconSize / 2 - mOffsetX,
+ newBounds.height() / 2 - mIconSize / 2 - mOffsetY);
+
+ // If this is the first frame, we need to trigger the veil's fade-in animation.
+ if (!mShown) {
+ startFadeAnimation(
+ true /* show */,
+ false /* releaseSurface */,
+ null /* finishedCallball */,
+ false /* addDelay */
+ );
+ mShown = true;
+ }
+ }
+
+ /** Called at the end of the swap animation. */
+ public void fadeOutVeilAndCleanUp(SurfaceControl.Transaction t) {
+ if (mVeilIconView == null) {
+ return;
+ }
+
+ // Recenter icon
+ t.setPosition(mIconLeash,
+ mInstantaneousBounds.width() / 2f - mIconSize / 2f,
+ mInstantaneousBounds.height() / 2f - mIconSize / 2f);
+
+ mIsCurrentlyChanging = false;
+ mOffsetX = 0;
+ mOffsetY = 0;
+ mInstantaneousBounds.setEmpty();
+
+ fadeOutDecor(() -> {}, true /* addDelay */);
+ }
+
/** Screenshot host leash and attach on it if meet some conditions */
public void screenshotIfNeeded(SurfaceControl.Transaction t) {
- if (!mShown && mIsResizing && !mOldMainBounds.equals(mResizingBounds)) {
+ if (!mShown && mIsCurrentlyChanging && !mOldMainBounds.equals(mInstantaneousBounds)) {
if (mScreenshotAnimator != null && mScreenshotAnimator.isRunning()) {
mScreenshotAnimator.cancel();
} else if (mScreenshot != null) {
@@ -386,7 +480,7 @@ public class SplitDecorManager extends WindowlessWindowManager {
public void setScreenshotIfNeeded(SurfaceControl screenshot, SurfaceControl.Transaction t) {
if (screenshot == null || !screenshot.isValid()) return;
- if (!mShown && mIsResizing && !mOldMainBounds.equals(mResizingBounds)) {
+ if (!mShown && mIsCurrentlyChanging && !mOldMainBounds.equals(mInstantaneousBounds)) {
if (mScreenshotAnimator != null && mScreenshotAnimator.isRunning()) {
mScreenshotAnimator.cancel();
} else if (mScreenshot != null) {
@@ -401,24 +495,35 @@ public class SplitDecorManager extends WindowlessWindowManager {
/** Fade-out decor surface with animation end callback, if decor is hidden, run the callback
* directly. */
- public void fadeOutDecor(Runnable finishedCallback) {
+ public void fadeOutDecor(Runnable finishedCallback, boolean addDelay) {
if (mShown) {
// If previous animation is running, just cancel it.
if (mFadeAnimator != null && mFadeAnimator.isRunning()) {
mFadeAnimator.cancel();
}
- startFadeAnimation(false /* show */, true, finishedCallback);
+ startFadeAnimation(
+ false /* show */, true /* releaseSurface */, finishedCallback, addDelay);
mShown = false;
} else {
if (finishedCallback != null) finishedCallback.run();
}
}
+ /**
+ * Fades the veil in or out. Called at the first frame of a movement or resize when a veil is
+ * needed (with show = true), and called again at the end (with show = false).
+ * @param addDelay If true, adds a short delay before fading out to get the app behind the veil
+ * time to redraw.
+ */
private void startFadeAnimation(boolean show, boolean releaseSurface,
- Runnable finishedCallback) {
+ Runnable finishedCallback, boolean addDelay) {
final SurfaceControl.Transaction animT = new SurfaceControl.Transaction();
+
mFadeAnimator = ValueAnimator.ofFloat(0f, 1f);
+ if (addDelay) {
+ mFadeAnimator.setStartDelay(VEIL_DELAY_DURATION);
+ }
mFadeAnimator.setDuration(FADE_DURATION);
mFadeAnimator.addUpdateListener(valueAnimator-> {
final float progress = (float) valueAnimator.getAnimatedValue();
@@ -481,8 +586,8 @@ public class SplitDecorManager extends WindowlessWindowManager {
}
if (mIcon != null) {
- mResizingIconView.setVisibility(View.GONE);
- mResizingIconView.setImageDrawable(null);
+ mVeilIconView.setVisibility(View.GONE);
+ mVeilIconView.setImageDrawable(null);
t.hide(mIconLeash);
mIcon = null;
}
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 51f9de8305f8..0e050694c733 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
@@ -53,6 +53,8 @@ import android.view.RoundedCorner;
import android.view.SurfaceControl;
import android.view.WindowInsets;
import android.view.WindowManager;
+import android.view.animation.Interpolator;
+import android.view.animation.PathInterpolator;
import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
@@ -68,10 +70,12 @@ import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayImeController;
import com.android.wm.shell.common.DisplayInsetsController;
import com.android.wm.shell.common.DisplayLayout;
+import com.android.wm.shell.common.pip.PipUtils;
import com.android.wm.shell.common.split.SplitScreenConstants.PersistentSnapPosition;
import com.android.wm.shell.common.split.SplitScreenConstants.SnapPosition;
import com.android.wm.shell.common.split.SplitScreenConstants.SplitPosition;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
+import com.android.wm.shell.splitscreen.StageTaskListener;
import java.io.PrintWriter;
import java.util.function.Consumer;
@@ -87,10 +91,29 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
public static final int PARALLAX_ALIGN_CENTER = 2;
public static final int FLING_RESIZE_DURATION = 250;
- private static final int FLING_SWITCH_DURATION = 350;
private static final int FLING_ENTER_DURATION = 450;
private static final int FLING_EXIT_DURATION = 450;
+ // Here are some (arbitrarily decided) layer definitions used during animations to make sure the
+ // layers stay in order. Note: This does not affect any other layer numbering systems because
+ // the layer system in WindowManager is local within sibling groups. So, for example, each
+ // "veil layer" defined here actually has two sub-layers; and *their* layer values, which we set
+ // in SplitDecorManager, are only important relative to each other.
+ public static final int DIVIDER_LAYER = 0;
+ public static final int FRONT_APP_VEIL_LAYER = DIVIDER_LAYER + 20;
+ public static final int FRONT_APP_LAYER = DIVIDER_LAYER + 10;
+ public static final int BEHIND_APP_VEIL_LAYER = DIVIDER_LAYER - 10;
+ public static final int BEHIND_APP_LAYER = DIVIDER_LAYER - 20;
+
+ // Animation specs for the swap animation
+ private static final int SWAP_ANIMATION_TOTAL_DURATION = 500;
+ private static final float SWAP_ANIMATION_SHRINK_DURATION = 83;
+ private static final float SWAP_ANIMATION_SHRINK_MARGIN_DP = 14;
+ private static final Interpolator SHRINK_INTERPOLATOR =
+ new PathInterpolator(0.2f, 0f, 0f, 1f);
+ private static final Interpolator GROW_INTERPOLATOR =
+ new PathInterpolator(0.45f, 0f, 0.5f, 1f);
+
private int mDividerWindowWidth;
private int mDividerInsets;
private int mDividerSize;
@@ -134,6 +157,7 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
private final InteractionJankMonitor mInteractionJankMonitor;
private boolean mIsLeftRightSplit;
private ValueAnimator mDividerFlingAnimator;
+ private AnimatorSet mSwapAnimator;
public SplitLayout(String windowName, Context context, Configuration configuration,
SplitLayoutHandler splitLayoutHandler,
@@ -579,6 +603,10 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
}
void onDoubleTappedDivider() {
+ if (isCurrentlySwapping()) {
+ return;
+ }
+
mSplitLayoutHandler.onDoubleTappedDivider();
}
@@ -685,36 +713,43 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
}
/** Switch both surface position with animation. */
- public void splitSwitching(SurfaceControl.Transaction t, SurfaceControl leash1,
- SurfaceControl leash2, Consumer<Rect> finishCallback) {
+ public void playSwapAnimation(SurfaceControl.Transaction t, StageTaskListener topLeftStage,
+ StageTaskListener bottomRightStage, Consumer<Rect> finishCallback) {
final Rect insets = getDisplayStableInsets(mContext);
+ // If we have insets in the direction of the swap, the animation won't look correct because
+ // window contents will shift and redraw again at the end. So we show a veil to hide that.
insets.set(mIsLeftRightSplit ? insets.left : 0, mIsLeftRightSplit ? 0 : insets.top,
mIsLeftRightSplit ? insets.right : 0, mIsLeftRightSplit ? 0 : insets.bottom);
+ final boolean shouldVeil =
+ insets.left != 0 || insets.top != 0 || insets.right != 0 || insets.bottom != 0;
final int dividerPos = mDividerSnapAlgorithm.calculateNonDismissingSnapTarget(
mIsLeftRightSplit ? mBounds2.width() : mBounds2.height()).position;
- final Rect distBounds1 = new Rect();
- final Rect distBounds2 = new Rect();
- final Rect distDividerBounds = new Rect();
- // Compute dist bounds.
- updateBounds(dividerPos, distBounds2, distBounds1, distDividerBounds,
+ final Rect endBounds1 = new Rect();
+ final Rect endBounds2 = new Rect();
+ final Rect endDividerBounds = new Rect();
+ // Compute destination bounds.
+ updateBounds(dividerPos, endBounds2, endBounds1, endDividerBounds,
false /* setEffectBounds */);
// Offset to real position under root container.
- distBounds1.offset(-mRootBounds.left, -mRootBounds.top);
- distBounds2.offset(-mRootBounds.left, -mRootBounds.top);
- distDividerBounds.offset(-mRootBounds.left, -mRootBounds.top);
-
- ValueAnimator animator1 = moveSurface(t, leash1, getRefBounds1(), distBounds1,
- -insets.left, -insets.top);
- ValueAnimator animator2 = moveSurface(t, leash2, getRefBounds2(), distBounds2,
- insets.left, insets.top);
- ValueAnimator animator3 = moveSurface(t, getDividerLeash(), getRefDividerBounds(),
- distDividerBounds, 0 /* offsetX */, 0 /* offsetY */);
-
- AnimatorSet set = new AnimatorSet();
- set.playTogether(animator1, animator2, animator3);
- set.setDuration(FLING_SWITCH_DURATION);
- set.addListener(new AnimatorListenerAdapter() {
+ endBounds1.offset(-mRootBounds.left, -mRootBounds.top);
+ endBounds2.offset(-mRootBounds.left, -mRootBounds.top);
+ endDividerBounds.offset(-mRootBounds.left, -mRootBounds.top);
+
+ ValueAnimator animator1 = moveSurface(t, topLeftStage, getRefBounds1(), endBounds1,
+ -insets.left, -insets.top, true /* roundCorners */, true /* isGoingBehind */,
+ shouldVeil);
+ ValueAnimator animator2 = moveSurface(t, bottomRightStage, getRefBounds2(), endBounds2,
+ insets.left, insets.top, true /* roundCorners */, false /* isGoingBehind */,
+ shouldVeil);
+ ValueAnimator animator3 = moveSurface(t, null /* stage */, getRefDividerBounds(),
+ endDividerBounds, 0 /* offsetX */, 0 /* offsetY */, false /* roundCorners */,
+ false /* isGoingBehind */, false /* addVeil */);
+
+ mSwapAnimator = new AnimatorSet();
+ mSwapAnimator.playTogether(animator1, animator2, animator3);
+ mSwapAnimator.setDuration(SWAP_ANIMATION_TOTAL_DURATION);
+ mSwapAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
mInteractionJankMonitor.begin(getDividerLeash(),
@@ -734,36 +769,144 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
mInteractionJankMonitor.cancel(CUJ_SPLIT_SCREEN_DOUBLE_TAP_DIVIDER);
}
});
- set.start();
+ mSwapAnimator.start();
+ }
+
+ /** Returns true if a swap animation is currently playing. */
+ public boolean isCurrentlySwapping() {
+ return mSwapAnimator != null && mSwapAnimator.isRunning();
}
- private ValueAnimator moveSurface(SurfaceControl.Transaction t, SurfaceControl leash,
- Rect start, Rect end, float offsetX, float offsetY) {
+ /**
+ * Animates a task leash across the screen. Currently used only for the swap animation.
+ *
+ * @param stage The stage holding the task being animated. If null, it is the divider.
+ * @param roundCorners Whether we should round the corners of the task while animating.
+ * @param isGoingBehind Whether we should a shrink-and-grow effect to the task while it is
+ * moving. (Simulates moving behind the divider.)
+ */
+ private ValueAnimator moveSurface(SurfaceControl.Transaction t, StageTaskListener stage,
+ Rect start, Rect end, float offsetX, float offsetY, boolean roundCorners,
+ boolean isGoingBehind, boolean addVeil) {
+ final boolean isApp = stage != null; // check if this is an app or a divider
+ final SurfaceControl leash = isApp ? stage.getRootLeash() : getDividerLeash();
+ final ActivityManager.RunningTaskInfo taskInfo = isApp ? stage.getRunningTaskInfo() : null;
+ final SplitDecorManager decorManager = isApp ? stage.getDecorManager() : null;
+
Rect tempStart = new Rect(start);
Rect tempEnd = new Rect(end);
final float diffX = tempEnd.left - tempStart.left;
final float diffY = tempEnd.top - tempStart.top;
final float diffWidth = tempEnd.width() - tempStart.width();
final float diffHeight = tempEnd.height() - tempStart.height();
+
+ // Get display measurements (for possible shrink animation).
+ final RoundedCorner roundedCorner = mSplitWindowManager.getDividerView().getDisplay()
+ .getRoundedCorner(0 /* position */);
+ float cornerRadius = roundedCorner == null ? 0 : roundedCorner.getRadius();
+ float shrinkMarginPx = PipUtils.dpToPx(
+ SWAP_ANIMATION_SHRINK_MARGIN_DP, mContext.getResources().getDisplayMetrics());
+ float shrinkAmountPx = shrinkMarginPx * 2;
+
+ // Timing calculations
+ float shrinkPortion = SWAP_ANIMATION_SHRINK_DURATION / SWAP_ANIMATION_TOTAL_DURATION;
+ float growPortion = 1 - shrinkPortion;
+
ValueAnimator animator = ValueAnimator.ofFloat(0, 1);
+ animator.setInterpolator(Interpolators.EMPHASIZED);
animator.addUpdateListener(animation -> {
if (leash == null) return;
+ if (roundCorners) {
+ // Add rounded corners to the task leash while it is animating.
+ t.setCornerRadius(leash, cornerRadius);
+ }
+
+ final float progress = (float) animation.getAnimatedValue();
+ float instantaneousX = tempStart.left + progress * diffX;
+ float instantaneousY = tempStart.top + progress * diffY;
+ int width = (int) (tempStart.width() + progress * diffWidth);
+ int height = (int) (tempStart.height() + progress * diffHeight);
+
+ if (isGoingBehind) {
+ float shrinkDiffX; // the position adjustments needed for this frame
+ float shrinkDiffY;
+ float shrinkScaleX; // the scale adjustments needed for this frame
+ float shrinkScaleY;
+
+ // Find the max amount we will be shrinking this leash, as a proportion (e.g. 0.1f).
+ float maxShrinkX = shrinkAmountPx / height;
+ float maxShrinkY = shrinkAmountPx / width;
+
+ // Find if we are in the shrinking part of the animation, or the growing part.
+ boolean shrinking = progress <= shrinkPortion;
+
+ if (shrinking) {
+ // Find how far into the shrink portion we are (e.g. 0.5f).
+ float shrinkProgress = progress / shrinkPortion;
+ // Find how much we should have progressed in shrinking the leash (e.g. 0.8f).
+ float interpolatedShrinkProgress =
+ SHRINK_INTERPOLATOR.getInterpolation(shrinkProgress);
+ // Find how much width proportion we should be taking off (e.g. 0.1f)
+ float widthProportionLost = maxShrinkX * interpolatedShrinkProgress;
+ shrinkScaleX = 1 - widthProportionLost;
+ // Find how much height proportion we should be taking off (e.g. 0.1f)
+ float heightProportionLost = maxShrinkY * interpolatedShrinkProgress;
+ shrinkScaleY = 1 - heightProportionLost;
+ // Add a small amount to the leash's position to keep the task centered.
+ shrinkDiffX = (width * widthProportionLost) / 2;
+ shrinkDiffY = (height * heightProportionLost) / 2;
+ } else {
+ // Find how far into the grow portion we are (e.g. 0.5f).
+ float growProgress = (progress - shrinkPortion) / growPortion;
+ // Find how much we should have progressed in growing the leash (e.g. 0.8f).
+ float interpolatedGrowProgress =
+ GROW_INTERPOLATOR.getInterpolation(growProgress);
+ // Find how much width proportion we should be taking off (e.g. 0.1f)
+ float widthProportionLost = maxShrinkX * (1 - interpolatedGrowProgress);
+ shrinkScaleX = 1 - widthProportionLost;
+ // Find how much height proportion we should be taking off (e.g. 0.1f)
+ float heightProportionLost = maxShrinkY * (1 - interpolatedGrowProgress);
+ shrinkScaleY = 1 - heightProportionLost;
+ // Add a small amount to the leash's position to keep the task centered.
+ shrinkDiffX = (width * widthProportionLost) / 2;
+ shrinkDiffY = (height * heightProportionLost) / 2;
+ }
+
+ instantaneousX += shrinkDiffX;
+ instantaneousY += shrinkDiffY;
+ width *= shrinkScaleX;
+ height *= shrinkScaleY;
+ // Set scale on the leash's contents.
+ t.setScale(leash, shrinkScaleX, shrinkScaleY);
+ }
+
+ // Set layers
+ if (taskInfo != null) {
+ t.setLayer(leash, isGoingBehind ? BEHIND_APP_LAYER : FRONT_APP_LAYER);
+ } else {
+ t.setLayer(leash, DIVIDER_LAYER);
+ }
- final float scale = (float) animation.getAnimatedValue();
- final float distX = tempStart.left + scale * diffX;
- final float distY = tempStart.top + scale * diffY;
- final int width = (int) (tempStart.width() + scale * diffWidth);
- final int height = (int) (tempStart.height() + scale * diffHeight);
if (offsetX == 0 && offsetY == 0) {
- t.setPosition(leash, distX, distY);
+ t.setPosition(leash, instantaneousX, instantaneousY);
+ mTempRect.set((int) instantaneousX, (int) instantaneousY,
+ (int) (instantaneousX + width), (int) (instantaneousY + height));
t.setWindowCrop(leash, width, height);
+ if (addVeil) {
+ decorManager.drawNextVeilFrameForSwapAnimation(
+ taskInfo, mTempRect, t, isGoingBehind, leash, 0, 0);
+ }
} else {
- final int diffOffsetX = (int) (scale * offsetX);
- final int diffOffsetY = (int) (scale * offsetY);
- t.setPosition(leash, distX + diffOffsetX, distY + diffOffsetY);
+ final int diffOffsetX = (int) (progress * offsetX);
+ final int diffOffsetY = (int) (progress * offsetY);
+ t.setPosition(leash, instantaneousX + diffOffsetX, instantaneousY + diffOffsetY);
mTempRect.set(0, 0, width, height);
mTempRect.offsetTo(-diffOffsetX, -diffOffsetY);
t.setCrop(leash, mTempRect);
+ if (addVeil) {
+ decorManager.drawNextVeilFrameForSwapAnimation(
+ taskInfo, mTempRect, t, isGoingBehind, leash, diffOffsetX, diffOffsetY);
+ }
}
t.apply();
});
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenConstants.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenConstants.java
index e8c809e5db4a..8c06de79ba76 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenConstants.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenConstants.java
@@ -29,6 +29,8 @@ import com.android.wm.shell.shared.TransitionUtil;
public class SplitScreenConstants {
/** Duration used for every split fade-in or fade-out. */
public static final int FADE_DURATION = 133;
+ /** Duration where we keep an app veiled to allow it to redraw itself behind the scenes. */
+ public static final int VEIL_DELAY_DURATION = 400;
/** Key for passing in widget intents when invoking split from launcher workspace. */
public static final String KEY_EXTRA_WIDGET_INTENT = "key_extra_widget_intent";
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
index 284620e7d0c4..da6221efdaee 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
@@ -632,6 +632,12 @@ public class PipController implements PipTransitionController.PipTransitionCallb
public void insetsChanged(InsetsState insetsState) {
DisplayLayout pendingLayout = mDisplayController
.getDisplayLayout(mPipDisplayLayoutState.getDisplayId());
+ if (pendingLayout == null) {
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "insetsChanged: no display layout for displayId=%d",
+ mPipDisplayLayoutState.getDisplayId());
+ return;
+ }
if (mIsInFixedRotation
|| mIsKeyguardShowingOrAnimating
|| pendingLayout.rotation()
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index a7551bddc42d..87dc16a79766 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -123,10 +123,10 @@ import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayImeController;
import com.android.wm.shell.common.DisplayInsetsController;
import com.android.wm.shell.common.LaunchAdjacentController;
-import com.android.wm.shell.common.ScreenshotUtils;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.TransactionPool;
+import com.android.wm.shell.common.split.SplitDecorManager;
import com.android.wm.shell.common.split.SplitLayout;
import com.android.wm.shell.common.split.SplitScreenConstants.PersistentSnapPosition;
import com.android.wm.shell.common.split.SplitScreenConstants.SplitPosition;
@@ -1010,40 +1010,41 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
mTempRect1.setEmpty();
final StageTaskListener topLeftStage =
mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mSideStage : mMainStage;
- final SurfaceControl topLeftScreenshot = ScreenshotUtils.takeScreenshot(t,
- topLeftStage.mRootLeash, mTempRect1, Integer.MAX_VALUE - 1);
final StageTaskListener bottomRightStage =
mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mMainStage : mSideStage;
- final SurfaceControl bottomRightScreenshot = ScreenshotUtils.takeScreenshot(t,
- bottomRightStage.mRootLeash, mTempRect1, Integer.MAX_VALUE - 1);
- mSplitLayout.splitSwitching(t, topLeftStage.mRootLeash, bottomRightStage.mRootLeash,
+
+ // Don't allow windows or divider to be focused during animation (mRootTaskInfo is the
+ // parent of all 3 leaves). We don't want the user to be able to tap and focus a window
+ // while it is moving across the screen, because granting focus also recalculates the
+ // layering order, which is in delicate balance during this animation.
+ WindowContainerTransaction noFocus = new WindowContainerTransaction();
+ noFocus.setFocusable(mRootTaskInfo.token, false);
+ mSyncQueue.queue(noFocus);
+
+ mSplitLayout.playSwapAnimation(t, topLeftStage, bottomRightStage,
insets -> {
+ // Runs at the end of the swap animation
+ SplitDecorManager decorManager1 = topLeftStage.getDecorManager();
+ SplitDecorManager decorManager2 = bottomRightStage.getDecorManager();
+
WindowContainerTransaction wct = new WindowContainerTransaction();
+
+ // Restore focus-ability to the windows and divider
+ wct.setFocusable(mRootTaskInfo.token, true);
+
setSideStagePosition(reverseSplitPosition(mSideStagePosition), wct);
mSyncQueue.queue(wct);
mSyncQueue.runInSync(st -> {
updateSurfaceBounds(mSplitLayout, st, false /* applyResizingOffset */);
- st.setPosition(topLeftScreenshot, -insets.left, -insets.top);
- st.setPosition(bottomRightScreenshot, insets.left, insets.top);
-
- final ValueAnimator va = ValueAnimator.ofFloat(1, 0);
- va.addUpdateListener(valueAnimator-> {
- final float progress = (float) valueAnimator.getAnimatedValue();
- t.setAlpha(topLeftScreenshot, progress);
- t.setAlpha(bottomRightScreenshot, progress);
- t.apply();
- });
- va.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(
- @androidx.annotation.NonNull Animator animation) {
- t.remove(topLeftScreenshot);
- t.remove(bottomRightScreenshot);
- t.apply();
- mTransactionPool.release(t);
- }
- });
- va.start();
+
+ // updateSurfaceBounds(), above, officially puts the two apps in their new
+ // stages. Starting on the next frame, all calculations are made using the
+ // new layouts/insets. So any follow-up animations on the same leashes below
+ // should contain some cleanup/repositioning to prevent jank.
+
+ // Play follow-up animations if needed
+ decorManager1.fadeOutVeilAndCleanUp(st);
+ decorManager2.fadeOutVeilAndCleanUp(st);
});
});
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
index d1ab3e96d4c2..f19eb3f8291e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
@@ -69,7 +69,7 @@ import java.util.function.Predicate;
*
* @see StageCoordinator
*/
-class StageTaskListener implements ShellTaskOrganizer.TaskListener {
+public class StageTaskListener implements ShellTaskOrganizer.TaskListener {
private static final String TAG = StageTaskListener.class.getSimpleName();
/** Callback interface for listening to changes in a split-screen stage. */
@@ -162,6 +162,18 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener {
return getChildTaskInfo(predicate) != null;
}
+ public SurfaceControl getRootLeash() {
+ return mRootLeash;
+ }
+
+ public ActivityManager.RunningTaskInfo getRunningTaskInfo() {
+ return mRootTaskInfo;
+ }
+
+ public SplitDecorManager getDecorManager() {
+ return mSplitDecorManager;
+ }
+
@Nullable
private ActivityManager.RunningTaskInfo getChildTaskInfo(
Predicate<ActivityManager.RunningTaskInfo> predicate) {
@@ -335,7 +347,7 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener {
void fadeOutDecor(Runnable finishedCallback) {
if (mSplitDecorManager != null) {
- mSplitDecorManager.fadeOutDecor(finishedCallback);
+ mSplitDecorManager.fadeOutDecor(finishedCallback, false /* addDelay */);
} else {
finishedCallback.run();
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragResizeWindowGeometryTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragResizeWindowGeometryTests.java
index 77337a03ef55..d8f395d76b39 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragResizeWindowGeometryTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragResizeWindowGeometryTests.java
@@ -56,7 +56,7 @@ import org.junit.runner.RunWith;
public class DragResizeWindowGeometryTests extends ShellTestCase {
private static final Size TASK_SIZE = new Size(500, 1000);
private static final int TASK_CORNER_RADIUS = 10;
- private static final int EDGE_RESIZE_THICKNESS = 15;
+ private static final int EDGE_RESIZE_THICKNESS = 12;
private static final int FINE_CORNER_SIZE = EDGE_RESIZE_THICKNESS * 2 + 10;
private static final int LARGE_CORNER_SIZE = FINE_CORNER_SIZE + 10;
private static final DragResizeWindowGeometry GEOMETRY = new DragResizeWindowGeometry(
diff --git a/media/jni/JetPlayer.h b/media/jni/JetPlayer.h
index bb569bcad7be..4cc266dec445 100644
--- a/media/jni/JetPlayer.h
+++ b/media/jni/JetPlayer.h
@@ -40,7 +40,7 @@ public:
static const int JET_NUMQUEUEDSEGMENT_UPDATE = 3;
static const int JET_PAUSE_UPDATE = 4;
- JetPlayer(void *javaJetPlayer,
+ explicit JetPlayer(void *javaJetPlayer,
int maxTracks = 32,
int trackBufferSize = 1200);
~JetPlayer();
@@ -69,7 +69,6 @@ private:
void fireUpdateOnStatusChange();
void fireEventsFromJetQueue();
- JetPlayer() {} // no default constructor
void dump();
void dumpJetStatus(S_JET_STATUS* pJetStatus);
@@ -96,7 +95,7 @@ private:
class JetPlayerThread : public Thread {
public:
- JetPlayerThread(JetPlayer *player) : mPlayer(player) {
+ explicit JetPlayerThread(JetPlayer *player) : mPlayer(player) {
}
protected:
@@ -106,8 +105,7 @@ private:
JetPlayer *mPlayer;
bool threadLoop() {
- int result;
- result = mPlayer->render();
+ mPlayer->render();
return false;
}
diff --git a/native/android/surface_control_input_receiver.cpp b/native/android/surface_control_input_receiver.cpp
index a84ec7309a62..7caa943c3e60 100644
--- a/native/android/surface_control_input_receiver.cpp
+++ b/native/android/surface_control_input_receiver.cpp
@@ -41,7 +41,7 @@ public:
const sp<IBinder>& clientToken, const sp<InputTransferToken>& inputTransferToken,
AInputReceiverCallbacks* callbacks)
: mCallbacks(callbacks),
- mInputConsumer(inputChannel, looper, *this),
+ mInputConsumer(inputChannel, looper, *this, nullptr),
mClientToken(clientToken),
mInputTransferToken(inputTransferToken) {}
diff --git a/packages/SettingsLib/SelectorWithWidgetPreference/Android.bp b/packages/SettingsLib/SelectorWithWidgetPreference/Android.bp
index 2fe446d24b34..f9fe03e06cb0 100644
--- a/packages/SettingsLib/SelectorWithWidgetPreference/Android.bp
+++ b/packages/SettingsLib/SelectorWithWidgetPreference/Android.bp
@@ -20,6 +20,7 @@ android_library {
static_libs: [
"androidx.preference_preference",
"SettingsLibSettingsTheme",
+ "settingslib_selectorwithwidgetpreference_flags_lib",
],
sdk_version: "system_current",
@@ -30,3 +31,24 @@ android_library {
"com.android.mediaprovider",
],
}
+
+aconfig_declarations {
+ name: "settingslib_selectorwithwidgetpreference_flags",
+ package: "com.android.settingslib.widget.selectorwithwidgetpreference.flags",
+ container: "system",
+ srcs: [
+ "aconfig/selectorwithwidgetpreference.aconfig",
+ ],
+}
+
+java_aconfig_library {
+ name: "settingslib_selectorwithwidgetpreference_flags_lib",
+ aconfig_declarations: "settingslib_selectorwithwidgetpreference_flags",
+
+ min_sdk_version: "30",
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.permission",
+ "com.android.mediaprovider",
+ ],
+}
diff --git a/packages/SettingsLib/SelectorWithWidgetPreference/aconfig/selectorwithwidgetpreference.aconfig b/packages/SettingsLib/SelectorWithWidgetPreference/aconfig/selectorwithwidgetpreference.aconfig
new file mode 100644
index 000000000000..70cda476bff6
--- /dev/null
+++ b/packages/SettingsLib/SelectorWithWidgetPreference/aconfig/selectorwithwidgetpreference.aconfig
@@ -0,0 +1,13 @@
+package: "com.android.settingslib.widget.selectorwithwidgetpreference.flags"
+container: "system"
+
+flag {
+ name: "allow_set_title_max_lines"
+ namespace: "accessibility"
+ description: "Allow changes to the title max lines so it's not always fixed to 2"
+ bug: "356726764"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
diff --git a/packages/SettingsLib/SelectorWithWidgetPreference/res/values/attrs.xml b/packages/SettingsLib/SelectorWithWidgetPreference/res/values/attrs.xml
new file mode 100644
index 000000000000..7ffde2578cc1
--- /dev/null
+++ b/packages/SettingsLib/SelectorWithWidgetPreference/res/values/attrs.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2024 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources>
+ <declare-styleable name="SelectorWithWidgetPreference">
+ <!-- The maximum number of lines for rendering the title. -->
+ <attr name="titleMaxLines" format="integer" />
+ </declare-styleable>
+</resources> \ No newline at end of file
diff --git a/packages/SettingsLib/SelectorWithWidgetPreference/src/com/android/settingslib/widget/SelectorWithWidgetPreference.java b/packages/SettingsLib/SelectorWithWidgetPreference/src/com/android/settingslib/widget/SelectorWithWidgetPreference.java
index f2ce8a965dfa..34de5c4a5d75 100644
--- a/packages/SettingsLib/SelectorWithWidgetPreference/src/com/android/settingslib/widget/SelectorWithWidgetPreference.java
+++ b/packages/SettingsLib/SelectorWithWidgetPreference/src/com/android/settingslib/widget/SelectorWithWidgetPreference.java
@@ -17,15 +17,21 @@
package com.android.settingslib.widget;
import android.content.Context;
+import android.content.res.TypedArray;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.View;
import android.widget.ImageView;
+import android.widget.TextView;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
import androidx.preference.CheckBoxPreference;
import androidx.preference.PreferenceViewHolder;
import com.android.settingslib.widget.preference.selector.R;
+import com.android.settingslib.widget.selectorwithwidgetpreference.flags.Flags;
/**
* Selector preference (checkbox or radio button) with an optional additional widget.
@@ -41,6 +47,8 @@ import com.android.settingslib.widget.preference.selector.R;
* on the right side that can open another page.
*/
public class SelectorWithWidgetPreference extends CheckBoxPreference {
+ @VisibleForTesting
+ static final int DEFAULT_MAX_LINES = 2;
/**
* Interface definition for a callback to be invoked when the preference is clicked.
@@ -63,6 +71,8 @@ public class SelectorWithWidgetPreference extends CheckBoxPreference {
private boolean mIsCheckBox = false; // whether to display this button as a checkbox
private View.OnClickListener mExtraWidgetOnClickListener;
+ private int mTitleMaxLines;
+
/**
* Perform inflation from XML and apply a class-specific base style.
@@ -74,9 +84,10 @@ public class SelectorWithWidgetPreference extends CheckBoxPreference {
* resource that supplies default values for the view. Can be 0 to not
* look for defaults.
*/
- public SelectorWithWidgetPreference(Context context, AttributeSet attrs, int defStyle) {
+ public SelectorWithWidgetPreference(@NonNull Context context, @Nullable AttributeSet attrs,
+ int defStyle) {
super(context, attrs, defStyle);
- init();
+ init(context, attrs, defStyle, /* defStyleRes= */ 0);
}
/**
@@ -88,7 +99,7 @@ public class SelectorWithWidgetPreference extends CheckBoxPreference {
*/
public SelectorWithWidgetPreference(Context context, AttributeSet attrs) {
super(context, attrs);
- init();
+ init(context, attrs, /* defStyleAttr= */ 0, /* defStyleRes= */ 0);
}
/**
@@ -100,7 +111,7 @@ public class SelectorWithWidgetPreference extends CheckBoxPreference {
public SelectorWithWidgetPreference(Context context, boolean isCheckbox) {
super(context, null);
mIsCheckBox = isCheckbox;
- init();
+ init(context, /* attrs= */ null, /* defStyleAttr= */ 0, /* defStyleRes= */ 0);
}
/**
@@ -161,6 +172,11 @@ public class SelectorWithWidgetPreference extends CheckBoxPreference {
mExtraWidgetContainer = holder.findViewById(R.id.selector_extra_widget_container);
setExtraWidgetOnClickListener(mExtraWidgetOnClickListener);
+
+ if (Flags.allowSetTitleMaxLines()) {
+ TextView title = (TextView) holder.findViewById(android.R.id.title);
+ title.setMaxLines(mTitleMaxLines);
+ }
}
/**
@@ -200,7 +216,8 @@ public class SelectorWithWidgetPreference extends CheckBoxPreference {
return mIsCheckBox;
}
- private void init() {
+ private void init(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr,
+ int defStyleRes) {
if (mIsCheckBox) {
setWidgetLayoutResource(R.layout.preference_widget_checkbox);
} else {
@@ -208,5 +225,16 @@ public class SelectorWithWidgetPreference extends CheckBoxPreference {
}
setLayoutResource(R.layout.preference_selector_with_widget);
setIconSpaceReserved(false);
+
+ if (Flags.allowSetTitleMaxLines()) {
+ final TypedArray a =
+ context.obtainStyledAttributes(
+ attrs, R.styleable.SelectorWithWidgetPreference, defStyleAttr,
+ defStyleRes);
+ mTitleMaxLines =
+ a.getInt(R.styleable.SelectorWithWidgetPreference_titleMaxLines,
+ DEFAULT_MAX_LINES);
+ a.recycle();
+ }
}
}
diff --git a/packages/SettingsLib/tests/robotests/Android.bp b/packages/SettingsLib/tests/robotests/Android.bp
index 75c40bfa8b60..f380e7f7e7a6 100644
--- a/packages/SettingsLib/tests/robotests/Android.bp
+++ b/packages/SettingsLib/tests/robotests/Android.bp
@@ -56,6 +56,7 @@ android_robolectric_test {
"flag-junit",
"settingslib_media_flags_lib",
"settingslib_illustrationpreference_flags_lib",
+ "settingslib_selectorwithwidgetpreference_flags_lib",
"testng", // TODO: remove once JUnit on Android provides assertThrows
],
java_resource_dirs: ["config"],
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/SelectorWithWidgetPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/SelectorWithWidgetPreferenceTest.java
index 60885f1079d3..243ce85bd579 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/SelectorWithWidgetPreferenceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/SelectorWithWidgetPreferenceTest.java
@@ -23,22 +23,31 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import android.app.Application;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
+import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
+import android.widget.TextView;
import androidx.preference.PreferenceViewHolder;
+import androidx.test.core.app.ApplicationProvider;
import com.android.settingslib.widget.preference.selector.R;
+import com.android.settingslib.widget.selectorwithwidgetpreference.flags.Flags;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.robolectric.Robolectric;
import org.robolectric.RobolectricTestRunner;
-import org.robolectric.RuntimeEnvironment;
@RunWith(RobolectricTestRunner.class)
public class SelectorWithWidgetPreferenceTest {
+ @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
private Application mContext;
private SelectorWithWidgetPreference mPreference;
@@ -55,7 +64,7 @@ public class SelectorWithWidgetPreferenceTest {
@Before
public void setUp() {
- mContext = RuntimeEnvironment.application;
+ mContext = ApplicationProvider.getApplicationContext();
mPreference = new SelectorWithWidgetPreference(mContext);
View view = LayoutInflater.from(mContext)
@@ -121,6 +130,60 @@ public class SelectorWithWidgetPreferenceTest {
}
@Test
+ @DisableFlags(Flags.FLAG_ALLOW_SET_TITLE_MAX_LINES)
+ public void onBindViewHolder_titleMaxLinesSet_flagOff_titleMaxLinesMatchesDefault() {
+ final int titleMaxLines = 5;
+ AttributeSet attributeSet = Robolectric.buildAttributeSet()
+ .addAttribute(R.attr.titleMaxLines, String.valueOf(titleMaxLines))
+ .build();
+ mPreference = new SelectorWithWidgetPreference(mContext, attributeSet);
+ View view = LayoutInflater.from(mContext)
+ .inflate(mPreference.getLayoutResource(), null /* root */);
+ PreferenceViewHolder preferenceViewHolder =
+ PreferenceViewHolder.createInstanceForTests(view);
+
+ mPreference.onBindViewHolder(preferenceViewHolder);
+
+ TextView title = (TextView) preferenceViewHolder.findViewById(android.R.id.title);
+ assertThat(title.getMaxLines()).isEqualTo(SelectorWithWidgetPreference.DEFAULT_MAX_LINES);
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ALLOW_SET_TITLE_MAX_LINES)
+ public void onBindViewHolder_noTitleMaxLinesSet_titleMaxLinesMatchesDefault() {
+ AttributeSet attributeSet = Robolectric.buildAttributeSet().build();
+ mPreference = new SelectorWithWidgetPreference(mContext, attributeSet);
+ View view = LayoutInflater.from(mContext)
+ .inflate(mPreference.getLayoutResource(), null /* root */);
+ PreferenceViewHolder preferenceViewHolder =
+ PreferenceViewHolder.createInstanceForTests(view);
+
+ mPreference.onBindViewHolder(preferenceViewHolder);
+
+ TextView title = (TextView) preferenceViewHolder.findViewById(android.R.id.title);
+ assertThat(title.getMaxLines()).isEqualTo(SelectorWithWidgetPreference.DEFAULT_MAX_LINES);
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ALLOW_SET_TITLE_MAX_LINES)
+ public void onBindViewHolder_titleMaxLinesSet_titleMaxLinesUpdated() {
+ final int titleMaxLines = 5;
+ AttributeSet attributeSet = Robolectric.buildAttributeSet()
+ .addAttribute(R.attr.titleMaxLines, String.valueOf(titleMaxLines))
+ .build();
+ mPreference = new SelectorWithWidgetPreference(mContext, attributeSet);
+ View view = LayoutInflater.from(mContext)
+ .inflate(mPreference.getLayoutResource(), null /* root */);
+ PreferenceViewHolder preferenceViewHolder =
+ PreferenceViewHolder.createInstanceForTests(view);
+
+ mPreference.onBindViewHolder(preferenceViewHolder);
+
+ TextView title = (TextView) preferenceViewHolder.findViewById(android.R.id.title);
+ assertThat(title.getMaxLines()).isEqualTo(titleMaxLines);
+ }
+
+ @Test
public void nullSummary_containerShouldBeGone() {
mPreference.setSummary(null);
View summaryContainer = new View(mContext);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/device_config_service.aconfig b/packages/SettingsProvider/src/com/android/providers/settings/device_config_service.aconfig
index f53dec6dc713..b1e6d6650226 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/device_config_service.aconfig
+++ b/packages/SettingsProvider/src/com/android/providers/settings/device_config_service.aconfig
@@ -28,6 +28,13 @@ flag {
}
flag {
+ name: "use_new_storage_value"
+ namespace: "core_experiments_team_internal"
+ description: "When enabled, read the new storage value in aconfig codegen, and actually use it."
+ bug: "312235596"
+}
+
+flag {
name: "load_apex_aconfig_protobufs"
namespace: "core_experiments_team_internal"
description: "When enabled, loads aconfig default values in apex flag protobufs into DeviceConfig on boot."
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index 3767a27c2e6f..e3c39d073125 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -475,18 +475,6 @@ flag {
}
flag {
- name: "centralized_status_bar_height_fix"
- namespace: "systemui"
- description: "Refactors shade header and keyguard status bar to read status bar dimens from a"
- " central place, instead of reading resources directly. This is to take into account display"
- " cutouts and other special cases. "
- bug: "317016114"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
name: "enable_layout_tracing"
namespace: "systemui"
description: "Enables detailed traversal slices during measure and layout in perfetto traces"
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/NotificationSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/NotificationSection.kt
index f0f407a52243..4e117d6ff4db 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/NotificationSection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/NotificationSection.kt
@@ -23,19 +23,16 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
-import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import com.android.compose.animation.scene.SceneScope
import com.android.compose.modifiers.thenIf
-import com.android.systemui.Flags
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.MigrateClocksToBlueprint
import com.android.systemui.keyguard.ui.composable.modifier.burnInAware
import com.android.systemui.keyguard.ui.viewmodel.AodBurnInViewModel
import com.android.systemui.keyguard.ui.viewmodel.BurnInParameters
import com.android.systemui.notifications.ui.composable.ConstrainedNotificationStack
-import com.android.systemui.res.R
import com.android.systemui.shade.LargeScreenHeaderHelper
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout
import com.android.systemui.statusbar.notification.stack.ui.view.NotificationScrollView
@@ -97,11 +94,7 @@ constructor(
}
val splitShadeTopMargin: Dp =
- if (Flags.centralizedStatusBarHeightFix()) {
- LargeScreenHeaderHelper.getLargeScreenHeaderHeight(LocalContext.current).dp
- } else {
- dimensionResource(id = R.dimen.large_screen_shade_header_height)
- }
+ LargeScreenHeaderHelper.getLargeScreenHeaderHeight(LocalContext.current).dp
ConstrainedNotificationStack(
stackScrollView = stackScrollView.get(),
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/AccessibilityQsShortcutsRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/AccessibilityQsShortcutsRepositoryImplTest.kt
index 40ea0a066338..460461a003f6 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/AccessibilityQsShortcutsRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/AccessibilityQsShortcutsRepositoryImplTest.kt
@@ -37,7 +37,6 @@ import org.mockito.junit.MockitoRule
@SmallTest
@RunWith(AndroidJUnit4::class)
-@android.platform.test.annotations.EnabledOnRavenwood
class AccessibilityQsShortcutsRepositoryImplTest : SysuiTestCase() {
@Rule @JvmField val mockitoRule: MockitoRule = MockitoJUnit.rule()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/ColorCorrectionRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/ColorCorrectionRepositoryImplTest.kt
index fa47a02d78c9..4e1f82c24bb6 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/ColorCorrectionRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/ColorCorrectionRepositoryImplTest.kt
@@ -37,7 +37,6 @@ import org.junit.runner.RunWith
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWith(AndroidJUnit4::class)
-@android.platform.test.annotations.EnabledOnRavenwood
class ColorCorrectionRepositoryImplTest : SysuiTestCase() {
private val testUser1 = UserHandle.of(1)!!
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/ColorInversionRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/ColorInversionRepositoryImplTest.kt
index 9c9ee53d9c56..b99dec44b519 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/ColorInversionRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/ColorInversionRepositoryImplTest.kt
@@ -37,7 +37,6 @@ import org.junit.runner.RunWith
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWith(AndroidJUnit4::class)
-@android.platform.test.annotations.EnabledOnRavenwood
class ColorInversionRepositoryImplTest : SysuiTestCase() {
private val testUser1 = UserHandle.of(1)!!
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/OneHandedModeRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/OneHandedModeRepositoryImplTest.kt
index c0d481c6e659..1378dac98eaa 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/OneHandedModeRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/OneHandedModeRepositoryImplTest.kt
@@ -35,7 +35,6 @@ import org.junit.runner.RunWith
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWith(AndroidJUnit4::class)
-@android.platform.test.annotations.EnabledOnRavenwood
class OneHandedModeRepositoryImplTest : SysuiTestCase() {
private val testUser1 = UserHandle.of(1)!!
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/UserA11yQsShortcutsRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/UserA11yQsShortcutsRepositoryTest.kt
index ed3b4c0fe322..ce22e288e292 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/UserA11yQsShortcutsRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/UserA11yQsShortcutsRepositoryTest.kt
@@ -31,7 +31,6 @@ import org.junit.runner.RunWith
@SmallTest
@RunWith(AndroidJUnit4::class)
-@android.platform.test.annotations.EnabledOnRavenwood
class UserA11yQsShortcutsRepositoryTest : SysuiTestCase() {
private val secureSettings = FakeSettings()
private val testDispatcher = StandardTestDispatcher()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/camera/data/repository/CameraAutoRotateRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/camera/data/repository/CameraAutoRotateRepositoryImplTest.kt
index 9cfa57257053..667d364ddc69 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/camera/data/repository/CameraAutoRotateRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/camera/data/repository/CameraAutoRotateRepositoryImplTest.kt
@@ -35,7 +35,6 @@ import org.junit.runner.RunWith
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWith(AndroidJUnit4::class)
-@android.platform.test.annotations.EnabledOnRavenwood
class CameraAutoRotateRepositoryImplTest : SysuiTestCase() {
private val kosmos = Kosmos()
private val testScope = kosmos.testScope
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalTutorialRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalTutorialRepositoryImplTest.kt
index 2911a50c2737..c37b33e52fa6 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalTutorialRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalTutorialRepositoryImplTest.kt
@@ -40,7 +40,6 @@ import org.mockito.MockitoAnnotations
@SmallTest
@RunWith(AndroidJUnit4::class)
-@android.platform.test.annotations.EnabledOnRavenwood
class CommunalTutorialRepositoryImplTest : SysuiTestCase() {
@Mock private lateinit var tableLogBuffer: TableLogBuffer
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/EditWidgetsActivityControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/EditWidgetsActivityControllerTest.kt
new file mode 100644
index 000000000000..50fdb31b0414
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/EditWidgetsActivityControllerTest.kt
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.app.Activity
+import android.app.Application.ActivityLifecycleCallbacks
+import android.os.Bundle
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.argumentCaptor
+import org.mockito.kotlin.clearInvocations
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.never
+import org.mockito.kotlin.verify
+
+@ExperimentalCoroutinesApi
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class EditWidgetsActivityControllerTest : SysuiTestCase() {
+ @Test
+ fun activityLifecycle_stoppedWhenNotWaitingForResult() {
+ val activity = mock<Activity>()
+ val controller = EditWidgetsActivity.ActivityController(activity)
+
+ val callbackCapture = argumentCaptor<ActivityLifecycleCallbacks>()
+ verify(activity).registerActivityLifecycleCallbacks(callbackCapture.capture())
+
+ callbackCapture.lastValue.onActivityStopped(activity)
+
+ verify(activity).finish()
+ }
+
+ @Test
+ fun activityLifecycle_notStoppedWhenNotWaitingForResult() {
+ val activity = mock<Activity>()
+ val controller = EditWidgetsActivity.ActivityController(activity)
+
+ val callbackCapture = argumentCaptor<ActivityLifecycleCallbacks>()
+ verify(activity).registerActivityLifecycleCallbacks(callbackCapture.capture())
+
+ controller.onWaitingForResult(true)
+ callbackCapture.lastValue.onActivityStopped(activity)
+
+ verify(activity, never()).finish()
+ }
+
+ @Test
+ fun activityLifecycle_stoppedAfterResultReturned() {
+ val activity = mock<Activity>()
+ val controller = EditWidgetsActivity.ActivityController(activity)
+
+ val callbackCapture = argumentCaptor<ActivityLifecycleCallbacks>()
+ verify(activity).registerActivityLifecycleCallbacks(callbackCapture.capture())
+
+ controller.onWaitingForResult(true)
+ controller.onWaitingForResult(false)
+ callbackCapture.lastValue.onActivityStopped(activity)
+
+ verify(activity).finish()
+ }
+
+ @Test
+ fun activityLifecycle_statePreservedThroughInstanceSave() {
+ val activity = mock<Activity>()
+ val bundle = Bundle(1)
+
+ run {
+ val controller = EditWidgetsActivity.ActivityController(activity)
+ val callbackCapture = argumentCaptor<ActivityLifecycleCallbacks>()
+ verify(activity).registerActivityLifecycleCallbacks(callbackCapture.capture())
+
+ controller.onWaitingForResult(true)
+ callbackCapture.lastValue.onActivitySaveInstanceState(activity, bundle)
+ }
+
+ clearInvocations(activity)
+
+ run {
+ val controller = EditWidgetsActivity.ActivityController(activity)
+ val callbackCapture = argumentCaptor<ActivityLifecycleCallbacks>()
+ verify(activity).registerActivityLifecycleCallbacks(callbackCapture.capture())
+
+ callbackCapture.lastValue.onActivityCreated(activity, bundle)
+ callbackCapture.lastValue.onActivityStopped(activity)
+
+ verify(activity, never()).finish()
+ }
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/education/data/repository/ContextualEducationRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/education/data/repository/ContextualEducationRepositoryTest.kt
index 331db525c4ee..f82a7b8df089 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/education/data/repository/ContextualEducationRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/education/data/repository/ContextualEducationRepositoryTest.kt
@@ -21,15 +21,14 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.SysuiTestableContext
-import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.contextualeducation.GestureType.BACK
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.education.data.model.GestureEduModel
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testDispatcher
import com.android.systemui.kosmos.testScope
import com.google.common.truth.Truth.assertThat
import java.io.File
-import java.time.Clock
-import java.time.Instant
import javax.inject.Provider
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.test.TestScope
@@ -44,13 +43,13 @@ import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class ContextualEducationRepositoryTest : SysuiTestCase() {
- private lateinit var underTest: ContextualEducationRepository
+ private lateinit var underTest: UserContextualEducationRepository
private val kosmos = Kosmos()
private val testScope = kosmos.testScope
private val dsScopeProvider: Provider<CoroutineScope> = Provider {
TestScope(kosmos.testDispatcher).backgroundScope
}
- private val clock: Clock = FakeEduClock(Instant.ofEpochMilli(1000))
+
private val testUserId = 1111
// For deleting any test files created after the test
@@ -61,8 +60,7 @@ class ContextualEducationRepositoryTest : SysuiTestCase() {
// Create TestContext here because TemporaryFolder.create() is called in @Before. It is
// needed before calling TemporaryFolder.newFolder().
val testContext = TestContext(context, tmpFolder.newFolder())
- val userRepository = UserContextualEducationRepository(testContext, dsScopeProvider)
- underTest = ContextualEducationRepositoryImpl(clock, userRepository)
+ underTest = UserContextualEducationRepository(testContext, dsScopeProvider)
underTest.setUser(testUserId)
}
@@ -70,7 +68,7 @@ class ContextualEducationRepositoryTest : SysuiTestCase() {
fun changeRetrievedValueForNewUser() =
testScope.runTest {
// Update data for old user.
- underTest.incrementSignalCount(BACK)
+ underTest.updateGestureEduModel(BACK) { it.copy(signalCount = 1) }
val model by collectLastValue(underTest.readGestureEduModelFlow(BACK))
assertThat(model?.signalCount).isEqualTo(1)
@@ -81,20 +79,17 @@ class ContextualEducationRepositoryTest : SysuiTestCase() {
}
@Test
- fun incrementSignalCount() =
- testScope.runTest {
- underTest.incrementSignalCount(BACK)
- val model by collectLastValue(underTest.readGestureEduModelFlow(BACK))
- assertThat(model?.signalCount).isEqualTo(1)
- }
-
- @Test
- fun dataAddedOnUpdateShortcutTriggerTime() =
+ fun dataChangedOnUpdate() =
testScope.runTest {
+ val newModel =
+ GestureEduModel(
+ signalCount = 2,
+ educationShownCount = 1,
+ lastShortcutTriggeredTime = kosmos.fakeEduClock.instant()
+ )
+ underTest.updateGestureEduModel(BACK) { newModel }
val model by collectLastValue(underTest.readGestureEduModelFlow(BACK))
- assertThat(model?.lastShortcutTriggeredTime).isNull()
- underTest.updateShortcutTriggerTime(BACK)
- assertThat(model?.lastShortcutTriggeredTime).isEqualTo(clock.instant())
+ assertThat(model).isEqualTo(newModel)
}
/** Test context which allows overriding getFilesDir path */
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractorTest.kt
index ae3302ca658d..1b4632a546d4 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractorTest.kt
@@ -19,10 +19,9 @@ package com.android.systemui.education.domain.interactor
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.contextualeducation.GestureType
import com.android.systemui.contextualeducation.GestureType.BACK
-import com.android.systemui.education.data.repository.contextualEducationRepository
+import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.kosmos.testScope
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
@@ -36,7 +35,7 @@ import org.junit.runner.RunWith
class KeyboardTouchpadEduInteractorTest : SysuiTestCase() {
private val kosmos = testKosmos()
private val testScope = kosmos.testScope
- private val repository = kosmos.contextualEducationRepository
+ private val contextualEduInteractor = kosmos.contextualEducationInteractor
private val underTest: KeyboardTouchpadEduInteractor = kosmos.keyboardTouchpadEduInteractor
@Before
@@ -55,7 +54,7 @@ class KeyboardTouchpadEduInteractorTest : SysuiTestCase() {
@Test
fun noEducationInfoBeforeMaxSignalCountReached() =
testScope.runTest {
- repository.incrementSignalCount(BACK)
+ contextualEduInteractor.incrementSignalCount(BACK)
val model by collectLastValue(underTest.educationTriggered)
assertThat(model).isNull()
}
@@ -64,7 +63,7 @@ class KeyboardTouchpadEduInteractorTest : SysuiTestCase() {
fun noEducationInfoWhenShortcutTriggeredPreviously() =
testScope.runTest {
val model by collectLastValue(underTest.educationTriggered)
- repository.updateShortcutTriggerTime(BACK)
+ contextualEduInteractor.updateShortcutTriggerTime(BACK)
tryTriggeringEducation(BACK)
assertThat(model).isNull()
}
@@ -72,7 +71,7 @@ class KeyboardTouchpadEduInteractorTest : SysuiTestCase() {
private suspend fun tryTriggeringEducation(gestureType: GestureType) {
// Increment max number of signal to try triggering education
for (i in 1..KeyboardTouchpadEduInteractor.MAX_SIGNAL_COUNT) {
- repository.incrementSignalCount(gestureType)
+ contextualEduInteractor.incrementSignalCount(gestureType)
}
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/domain/interactor/IconTilesInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/domain/interactor/IconTilesInteractorTest.kt
index c3a5df06e2a4..661d4b01a1b2 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/domain/interactor/IconTilesInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/domain/interactor/IconTilesInteractorTest.kt
@@ -67,19 +67,25 @@ class IconTilesInteractorTest : SysuiTestCase() {
}
}
+ @OptIn(ExperimentalCoroutinesApi::class)
@Test
fun resize_updatesSharedPreferences() =
with(kosmos) {
testScope.runTest {
+ qsPreferencesRepository.setLargeTilesSpecs(setOf())
+ runCurrent()
+
val latest by collectLastValue(qsPreferencesRepository.largeTilesSpecs)
val spec = TileSpec.create("large")
// Assert that the tile is added to the large tiles after resizing
- underTest.resize(spec, toIcon = false)
+ underTest.resize(spec)
+ runCurrent()
assertThat(latest).contains(spec)
// Assert that the tile is removed from the large tiles after resizing
- underTest.resize(spec, toIcon = true)
+ underTest.resize(spec)
+ runCurrent()
assertThat(latest).doesNotContain(spec)
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/compose/DragAndDropStateTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/compose/DragAndDropStateTest.kt
index 45262ca0587c..b2f5765d0bc4 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/compose/DragAndDropStateTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/compose/DragAndDropStateTest.kt
@@ -22,6 +22,8 @@ import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.common.shared.model.Text
+import com.android.systemui.qs.panels.shared.model.SizedTile
+import com.android.systemui.qs.panels.shared.model.SizedTileImpl
import com.android.systemui.qs.panels.ui.viewmodel.EditTileViewModel
import com.android.systemui.qs.pipeline.shared.TileSpec
import com.google.common.truth.Truth.assertThat
@@ -37,15 +39,15 @@ class DragAndDropStateTest : SysuiTestCase() {
@Test
fun isMoving_returnsCorrectValue() {
// Asserts no tiles is moving
- TestEditTiles.forEach { assertThat(underTest.isMoving(it.tileSpec)).isFalse() }
+ TestEditTiles.forEach { assertThat(underTest.isMoving(it.tile.tileSpec)).isFalse() }
// Start the drag movement
underTest.onStarted(TestEditTiles[0])
// Assert that the correct tile is marked as moving
TestEditTiles.forEach {
- assertThat(underTest.isMoving(it.tileSpec))
- .isEqualTo(TestEditTiles[0].tileSpec == it.tileSpec)
+ assertThat(underTest.isMoving(it.tile.tileSpec))
+ .isEqualTo(TestEditTiles[0].tile.tileSpec == it.tile.tileSpec)
}
}
@@ -55,11 +57,11 @@ class DragAndDropStateTest : SysuiTestCase() {
underTest.onStarted(TestEditTiles[0])
// Move the tile to the end of the list
- underTest.onMoved(listState.tiles[5].tileSpec)
+ underTest.onMoved(listState.tiles[5].tile.tileSpec)
assertThat(underTest.currentPosition()).isEqualTo(5)
// Move the tile to the middle of the list
- underTest.onMoved(listState.tiles[2].tileSpec)
+ underTest.onMoved(listState.tiles[2].tile.tileSpec)
assertThat(underTest.currentPosition()).isEqualTo(2)
}
@@ -69,13 +71,13 @@ class DragAndDropStateTest : SysuiTestCase() {
underTest.onStarted(TestEditTiles[0])
// Move the tile to the end of the list
- underTest.onMoved(listState.tiles[5].tileSpec)
+ underTest.onMoved(listState.tiles[5].tile.tileSpec)
// Drop the tile
underTest.onDrop()
// Asserts no tiles is moving
- TestEditTiles.forEach { assertThat(underTest.isMoving(it.tileSpec)).isFalse() }
+ TestEditTiles.forEach { assertThat(underTest.isMoving(it.tile.tileSpec)).isFalse() }
}
@Test
@@ -87,19 +89,24 @@ class DragAndDropStateTest : SysuiTestCase() {
underTest.movedOutOfBounds()
// Asserts the moving tile is not current
- assertThat(listState.tiles.firstOrNull { it.tileSpec == TestEditTiles[0].tileSpec })
+ assertThat(
+ listState.tiles.firstOrNull { it.tile.tileSpec == TestEditTiles[0].tile.tileSpec }
+ )
.isNull()
}
companion object {
- private fun createEditTile(tileSpec: String): EditTileViewModel {
- return EditTileViewModel(
- tileSpec = TileSpec.create(tileSpec),
- icon = Icon.Resource(0, null),
- label = Text.Loaded("unused"),
- appName = null,
- isCurrent = true,
- availableEditActions = emptySet(),
+ private fun createEditTile(tileSpec: String): SizedTile<EditTileViewModel> {
+ return SizedTileImpl(
+ EditTileViewModel(
+ tileSpec = TileSpec.create(tileSpec),
+ icon = Icon.Resource(0, null),
+ label = Text.Loaded("unused"),
+ appName = null,
+ isCurrent = true,
+ availableEditActions = emptySet(),
+ ),
+ 1,
)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/compose/EditTileListStateTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/compose/EditTileListStateTest.kt
index e76d3892cf53..a3a6a33f6408 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/compose/EditTileListStateTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/compose/EditTileListStateTest.kt
@@ -21,6 +21,8 @@ import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.common.shared.model.Text
+import com.android.systemui.qs.panels.shared.model.SizedTile
+import com.android.systemui.qs.panels.shared.model.SizedTileImpl
import com.android.systemui.qs.panels.ui.viewmodel.EditTileViewModel
import com.android.systemui.qs.pipeline.shared.TileSpec
import com.google.common.truth.Truth.assertThat
@@ -35,7 +37,7 @@ class EditTileListStateTest : SysuiTestCase() {
@Test
fun movingNonExistentTile_tileAdded() {
val newTile = createEditTile("other_tile", false)
- underTest.move(newTile, TestEditTiles[0].tileSpec)
+ underTest.move(newTile, TestEditTiles[0].tile.tileSpec)
assertThat(underTest.tiles[0]).isEqualTo(newTile)
assertThat(underTest.tiles.subList(1, underTest.tiles.size))
@@ -51,7 +53,7 @@ class EditTileListStateTest : SysuiTestCase() {
@Test
fun movingTileToItself_listUnchanged() {
- underTest.move(TestEditTiles[0], TestEditTiles[0].tileSpec)
+ underTest.move(TestEditTiles[0], TestEditTiles[0].tile.tileSpec)
assertThat(underTest.tiles).containsExactly(*TestEditTiles.toTypedArray())
}
@@ -59,7 +61,7 @@ class EditTileListStateTest : SysuiTestCase() {
@Test
fun movingTileToSameSection_listUpdates() {
// Move tile at index 0 to index 1. Tile 0 should remain current.
- underTest.move(TestEditTiles[0], TestEditTiles[1].tileSpec)
+ underTest.move(TestEditTiles[0], TestEditTiles[1].tile.tileSpec)
// Assert the tiles 0 and 1 have changed places.
assertThat(underTest.tiles[0]).isEqualTo(TestEditTiles[1])
@@ -72,21 +74,27 @@ class EditTileListStateTest : SysuiTestCase() {
fun removingTile_listUpdates() {
// Remove tile at index 0
- underTest.remove(TestEditTiles[0].tileSpec)
+ underTest.remove(TestEditTiles[0].tile.tileSpec)
// Assert the tile was removed
assertThat(underTest.tiles).containsExactly(*TestEditTiles.subList(1, 6).toTypedArray())
}
companion object {
- private fun createEditTile(tileSpec: String, isCurrent: Boolean): EditTileViewModel {
- return EditTileViewModel(
- tileSpec = TileSpec.create(tileSpec),
- icon = Icon.Resource(0, null),
- label = Text.Loaded("unused"),
- appName = null,
- isCurrent = isCurrent,
- availableEditActions = emptySet(),
+ private fun createEditTile(
+ tileSpec: String,
+ isCurrent: Boolean
+ ): SizedTile<EditTileViewModel> {
+ return SizedTileImpl(
+ EditTileViewModel(
+ tileSpec = TileSpec.create(tileSpec),
+ icon = Icon.Resource(0, null),
+ label = Text.Loaded("unused"),
+ appName = null,
+ isCurrent = isCurrent,
+ availableEditActions = emptySet(),
+ ),
+ 1,
)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/compose/PaginatableGridLayoutTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/compose/PaginatableGridLayoutTest.kt
index 6df3f8d1bdd5..0d93686714bf 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/compose/PaginatableGridLayoutTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/compose/PaginatableGridLayoutTest.kt
@@ -19,7 +19,7 @@ package com.android.systemui.qs.panels.ui.compose
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.qs.panels.shared.model.SizedTile
+import com.android.systemui.qs.panels.shared.model.SizedTileImpl
import com.android.systemui.qs.panels.ui.viewmodel.MockTileViewModel
import com.android.systemui.qs.pipeline.shared.TileSpec
import com.google.common.truth.Truth.assertThat
@@ -72,10 +72,10 @@ class PaginatableGridLayoutTest : SysuiTestCase() {
}
companion object {
- fun extraLargeTile() = SizedTile(MockTileViewModel(TileSpec.create("XLarge")), 3)
+ fun extraLargeTile() = SizedTileImpl(MockTileViewModel(TileSpec.create("XLarge")), 3)
- fun largeTile() = SizedTile(MockTileViewModel(TileSpec.create("large")), 2)
+ fun largeTile() = SizedTileImpl(MockTileViewModel(TileSpec.create("large")), 2)
- fun smallTile() = SizedTile(MockTileViewModel(TileSpec.create("small")), 1)
+ fun smallTile() = SizedTileImpl(MockTileViewModel(TileSpec.create("small")), 1)
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/AutoAddableSettingTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/AutoAddableSettingTest.kt
index cfb84a7a6709..d153e9d1d361 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/AutoAddableSettingTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/AutoAddableSettingTest.kt
@@ -16,7 +16,6 @@
package com.android.systemui.qs.pipeline.domain.autoaddable
-import android.platform.test.annotations.EnabledOnRavenwood
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
@@ -36,7 +35,6 @@ import org.junit.runner.RunWith
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
-@EnabledOnRavenwood
@RunWith(AndroidJUnit4::class)
class AutoAddableSettingTest : SysuiTestCase() {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/NotificationShadeWindowModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/NotificationShadeWindowModelTest.kt
index add33dacf669..6a886643cebb 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/NotificationShadeWindowModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/NotificationShadeWindowModelTest.kt
@@ -43,7 +43,7 @@ class NotificationShadeWindowModelTest : SysuiTestCase() {
}
@Test
- fun transitionToOccluded() =
+ fun transitionToOccludedByOCCLUDEDTransition() =
testScope.runTest {
val isKeyguardOccluded by collectLastValue(underTest.isKeyguardOccluded)
assertThat(isKeyguardOccluded).isFalse()
@@ -62,4 +62,25 @@ class NotificationShadeWindowModelTest : SysuiTestCase() {
)
assertThat(isKeyguardOccluded).isFalse()
}
+
+ @Test
+ fun transitionToOccludedByDREAMINGTransition() =
+ testScope.runTest {
+ val isKeyguardOccluded by collectLastValue(underTest.isKeyguardOccluded)
+ assertThat(isKeyguardOccluded).isFalse()
+
+ keyguardTransitionRepository.sendTransitionSteps(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.DREAMING,
+ testScope,
+ )
+ assertThat(isKeyguardOccluded).isTrue()
+
+ keyguardTransitionRepository.sendTransitionSteps(
+ from = KeyguardState.DREAMING,
+ to = KeyguardState.AOD,
+ testScope,
+ )
+ assertThat(isKeyguardOccluded).isFalse()
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
index 9fea7a2bfbf6..2fb9e1e038c8 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
@@ -172,7 +172,7 @@ class SharedNotificationContainerViewModelTest(flags: FlagsParameterization) : S
}
@Test
- fun validatePaddingTopInSplitShade_refactorFlagOn_usesLargeHeaderHelper() =
+ fun validatePaddingTopInSplitShade_usesLargeHeaderHelper() =
testScope.runTest {
whenever(largeScreenHeaderHelper.getLargeScreenHeaderHeight()).thenReturn(5)
overrideResource(R.bool.config_use_split_notification_shade, true)
diff --git a/packages/SystemUI/res/layout/chipbar.xml b/packages/SystemUI/res/layout/chipbar.xml
index 8fa975be43d2..e1b8ab469765 100644
--- a/packages/SystemUI/res/layout/chipbar.xml
+++ b/packages/SystemUI/res/layout/chipbar.xml
@@ -49,7 +49,7 @@
android:alpha="0.0"
/>
- <!-- LINT.IfChange textColor -->
+ <!-- LINT.IfChange -->
<TextView
android:id="@+id/text"
android:layout_width="0dp"
@@ -78,7 +78,7 @@
android:layout_height="@dimen/chipbar_end_icon_size"
android:layout_marginStart="@dimen/chipbar_end_item_start_margin"
android:src="@drawable/ic_warning"
- android:tint="@color/GM2_red_600"
+ android:tint="@color/GM2_red_800"
android:alpha="0.0"
/>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index 0350cd7dab98..8cf0fb2537cc 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -160,8 +160,8 @@
<color name="GM2_red_300">#F28B82</color>
<color name="GM2_red_500">#EA4335</color>
- <color name="GM2_red_600">#B3261E</color>
<color name="GM2_red_700">#C5221F</color>
+ <color name="GM2_red_800">#B3261E</color>
<color name="GM2_blue_300">#8AB4F8</color>
diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt
index 03ef17b6ec5b..2bcbc9aa74ac 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt
@@ -16,7 +16,10 @@
package com.android.systemui.communal.widgets
+import android.app.Activity
+import android.app.Application.ActivityLifecycleCallbacks
import android.content.Intent
+import android.content.IntentSender
import android.os.Bundle
import android.os.RemoteException
import android.util.Log
@@ -66,12 +69,78 @@ constructor(
const val EXTRA_OPEN_WIDGET_PICKER_ON_START = "open_widget_picker_on_start"
}
+ /**
+ * [ActivityController] handles closing the activity in the case it is backgrounded without
+ * waiting for an activity result
+ */
+ class ActivityController(activity: Activity) {
+ companion object {
+ private const val STATE_EXTRA_IS_WAITING_FOR_RESULT = "extra_is_waiting_for_result"
+ }
+
+ private var waitingForResult: Boolean = false
+
+ init {
+ activity.registerActivityLifecycleCallbacks(
+ object : ActivityLifecycleCallbacks {
+ override fun onActivityCreated(
+ activity: Activity,
+ savedInstanceState: Bundle?
+ ) {
+ waitingForResult =
+ savedInstanceState?.getBoolean(STATE_EXTRA_IS_WAITING_FOR_RESULT)
+ ?: false
+ }
+
+ override fun onActivityStarted(activity: Activity) {
+ // Nothing to implement.
+ }
+
+ override fun onActivityResumed(activity: Activity) {
+ // Nothing to implement.
+ }
+
+ override fun onActivityPaused(activity: Activity) {
+ // Nothing to implement.
+ }
+
+ override fun onActivityStopped(activity: Activity) {
+ // If we're not backgrounded due to waiting for a resul (either widget
+ // selection
+ // or configuration), finish activity.
+ if (!waitingForResult) {
+ activity.finish()
+ }
+ }
+
+ override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {
+ outState.putBoolean(STATE_EXTRA_IS_WAITING_FOR_RESULT, waitingForResult)
+ }
+
+ override fun onActivityDestroyed(activity: Activity) {
+ // Nothing to implement.
+ }
+ }
+ )
+ }
+
+ /**
+ * Invoked when waiting for an activity result changes, either initiating such wait or
+ * finishing due to the return of a result.
+ */
+ fun onWaitingForResult(waitingForResult: Boolean) {
+ this.waitingForResult = waitingForResult
+ }
+ }
+
private val logger = Logger(logBuffer, "EditWidgetsActivity")
private val widgetConfigurator by lazy { widgetConfiguratorFactory.create(this) }
private var shouldOpenWidgetPickerOnStart = false
+ private val activityController: ActivityController = ActivityController(this)
+
private val addWidgetActivityLauncher: ActivityResultLauncher<Intent> =
registerForActivityResult(StartActivityForResult()) { result ->
when (result.resultCode) {
@@ -154,6 +223,13 @@ constructor(
// edit mode
communalViewModel.currentScene.first { it == CommunalScenes.Blank }
communalViewModel.setEditModeState(EditModeState.SHOWING)
+
+ // Show the widget picker, if necessary, after the edit activity has animated in.
+ // Waiting until after the activity has appeared avoids transitions issues.
+ if (shouldOpenWidgetPickerOnStart) {
+ onOpenWidgetPicker()
+ shouldOpenWidgetPickerOnStart = false
+ }
}
}
}
@@ -186,7 +262,34 @@ constructor(
}
}
+ override fun startActivityForResult(intent: Intent, requestCode: Int, options: Bundle?) {
+ activityController.onWaitingForResult(true)
+ super.startActivityForResult(intent, requestCode, options)
+ }
+
+ override fun startIntentSenderForResult(
+ intent: IntentSender,
+ requestCode: Int,
+ fillInIntent: Intent?,
+ flagsMask: Int,
+ flagsValues: Int,
+ extraFlags: Int,
+ options: Bundle?
+ ) {
+ activityController.onWaitingForResult(true)
+ super.startIntentSenderForResult(
+ intent,
+ requestCode,
+ fillInIntent,
+ flagsMask,
+ flagsValues,
+ extraFlags,
+ options
+ )
+ }
+
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
+ activityController.onWaitingForResult(false)
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == WidgetConfigurationController.REQUEST_CODE) {
widgetConfigurator.setConfigurationResult(resultCode)
@@ -198,11 +301,6 @@ constructor(
communalViewModel.setEditActivityShowing(true)
- if (shouldOpenWidgetPickerOnStart) {
- onOpenWidgetPicker()
- shouldOpenWidgetPickerOnStart = false
- }
-
logger.i("Starting the communal widget editor activity")
uiEventLogger.log(CommunalUiEvent.COMMUNAL_HUB_EDIT_MODE_SHOWN)
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt b/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt
index 42866465a0cc..b0f2c18db565 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt
@@ -30,7 +30,7 @@ import com.android.systemui.dreams.AssistantAttentionMonitor
import com.android.systemui.dreams.DreamMonitor
import com.android.systemui.dreams.homecontrols.HomeControlsDreamStartable
import com.android.systemui.globalactions.GlobalActionsComponent
-import com.android.systemui.inputdevice.oobe.KeyboardTouchpadOobeTutorialCoreStartable
+import com.android.systemui.inputdevice.tutorial.KeyboardTouchpadTutorialCoreStartable
import com.android.systemui.keyboard.KeyboardUI
import com.android.systemui.keyboard.PhysicalKeyboardCoreStartable
import com.android.systemui.keyguard.KeyguardViewConfigurator
@@ -258,9 +258,9 @@ abstract class SystemUICoreStartableModule {
@Binds
@IntoMap
- @ClassKey(KeyboardTouchpadOobeTutorialCoreStartable::class)
- abstract fun bindOobeSchedulerCoreStartable(
- listener: KeyboardTouchpadOobeTutorialCoreStartable
+ @ClassKey(KeyboardTouchpadTutorialCoreStartable::class)
+ abstract fun bindKeyboardTouchpadTutorialCoreStartable(
+ listener: KeyboardTouchpadTutorialCoreStartable
): CoreStartable
@Binds
diff --git a/packages/SystemUI/src/com/android/systemui/education/dagger/ContextualEducationModule.kt b/packages/SystemUI/src/com/android/systemui/education/dagger/ContextualEducationModule.kt
index 532b123663ad..096556fed258 100644
--- a/packages/SystemUI/src/com/android/systemui/education/dagger/ContextualEducationModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/education/dagger/ContextualEducationModule.kt
@@ -18,10 +18,10 @@ package com.android.systemui.education.dagger
import com.android.systemui.CoreStartable
import com.android.systemui.Flags
-import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.contextualeducation.GestureType
+import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.education.data.repository.ContextualEducationRepository
-import com.android.systemui.education.data.repository.ContextualEducationRepositoryImpl
+import com.android.systemui.education.data.repository.UserContextualEducationRepository
import com.android.systemui.education.domain.interactor.ContextualEducationInteractor
import com.android.systemui.education.domain.interactor.KeyboardTouchpadEduInteractor
import com.android.systemui.education.domain.interactor.KeyboardTouchpadEduStatsInteractor
@@ -42,7 +42,7 @@ import kotlinx.coroutines.SupervisorJob
interface ContextualEducationModule {
@Binds
fun bindContextualEducationRepository(
- impl: ContextualEducationRepositoryImpl
+ impl: UserContextualEducationRepository
): ContextualEducationRepository
@Qualifier annotation class EduDataStoreScope
diff --git a/packages/SystemUI/src/com/android/systemui/education/data/repository/ContextualEducationRepository.kt b/packages/SystemUI/src/com/android/systemui/education/data/repository/ContextualEducationRepository.kt
deleted file mode 100644
index 52ccba4b65c7..000000000000
--- a/packages/SystemUI/src/com/android/systemui/education/data/repository/ContextualEducationRepository.kt
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.education.data.repository
-
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.contextualeducation.GestureType
-import com.android.systemui.education.dagger.ContextualEducationModule.EduClock
-import com.android.systemui.education.data.model.GestureEduModel
-import java.time.Clock
-import javax.inject.Inject
-import kotlinx.coroutines.flow.Flow
-
-/** Encapsulates the functions of ContextualEducationRepository. */
-interface ContextualEducationRepository {
- fun setUser(userId: Int)
-
- fun readGestureEduModelFlow(gestureType: GestureType): Flow<GestureEduModel>
-
- suspend fun incrementSignalCount(gestureType: GestureType)
-
- suspend fun updateShortcutTriggerTime(gestureType: GestureType)
-}
-
-/**
- * Provide methods to read and update on field level and allow setting datastore when user is
- * changed
- */
-@SysUISingleton
-class ContextualEducationRepositoryImpl
-@Inject
-constructor(
- @EduClock private val clock: Clock,
- private val userEduRepository: UserContextualEducationRepository
-) : ContextualEducationRepository {
- /** To change data store when user is changed */
- override fun setUser(userId: Int) = userEduRepository.setUser(userId)
-
- override fun readGestureEduModelFlow(gestureType: GestureType) =
- userEduRepository.readGestureEduModelFlow(gestureType)
-
- override suspend fun incrementSignalCount(gestureType: GestureType) {
- userEduRepository.updateGestureEduModel(gestureType) {
- it.copy(signalCount = it.signalCount + 1)
- }
- }
-
- override suspend fun updateShortcutTriggerTime(gestureType: GestureType) {
- userEduRepository.updateGestureEduModel(gestureType) {
- it.copy(lastShortcutTriggeredTime = clock.instant())
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/education/data/repository/UserContextualEducationRepository.kt b/packages/SystemUI/src/com/android/systemui/education/data/repository/UserContextualEducationRepository.kt
index 4b37b29e88a5..22ba4ad648f8 100644
--- a/packages/SystemUI/src/com/android/systemui/education/data/repository/UserContextualEducationRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/education/data/repository/UserContextualEducationRepository.kt
@@ -25,9 +25,9 @@ import androidx.datastore.preferences.core.edit
import androidx.datastore.preferences.core.intPreferencesKey
import androidx.datastore.preferences.core.longPreferencesKey
import androidx.datastore.preferences.preferencesDataStoreFile
+import com.android.systemui.contextualeducation.GestureType
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.contextualeducation.GestureType
import com.android.systemui.education.dagger.ContextualEducationModule.EduDataStoreScope
import com.android.systemui.education.data.model.GestureEduModel
import java.time.Instant
@@ -43,10 +43,24 @@ import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.map
/**
- * A contextual education repository to:
- * 1) store education data per user
- * 2) provide methods to read and update data on model-level
- * 3) provide method to enable changing datastore when user is changed
+ * Allows to:
+ * 1) read and update data on model-level
+ * 2) change data store when user is changed
+ */
+interface ContextualEducationRepository {
+ fun setUser(userId: Int)
+
+ fun readGestureEduModelFlow(gestureType: GestureType): Flow<GestureEduModel>
+
+ suspend fun updateGestureEduModel(
+ gestureType: GestureType,
+ transform: (GestureEduModel) -> GestureEduModel
+ )
+}
+
+/**
+ * Implementation of [ContextualEducationRepository] that uses [androidx.datastore.preferences.core]
+ * for storage. Data is stored per user.
*/
@SysUISingleton
class UserContextualEducationRepository
@@ -54,7 +68,7 @@ class UserContextualEducationRepository
constructor(
@Application private val applicationContext: Context,
@EduDataStoreScope private val dataStoreScopeProvider: Provider<CoroutineScope>
-) {
+) : ContextualEducationRepository {
companion object {
const val SIGNAL_COUNT_SUFFIX = "_SIGNAL_COUNT"
const val NUMBER_OF_EDU_SHOWN_SUFFIX = "_NUMBER_OF_EDU_SHOWN"
@@ -70,7 +84,7 @@ constructor(
@OptIn(kotlinx.coroutines.ExperimentalCoroutinesApi::class)
private val prefData: Flow<Preferences> = datastore.filterNotNull().flatMapLatest { it.data }
- internal fun setUser(userId: Int) {
+ override fun setUser(userId: Int) {
dataStoreScope?.cancel()
val newDsScope = dataStoreScopeProvider.get()
datastore.value =
@@ -85,7 +99,7 @@ constructor(
dataStoreScope = newDsScope
}
- internal fun readGestureEduModelFlow(gestureType: GestureType): Flow<GestureEduModel> =
+ override fun readGestureEduModelFlow(gestureType: GestureType): Flow<GestureEduModel> =
prefData.map { preferences -> getGestureEduModel(gestureType, preferences) }
private fun getGestureEduModel(
@@ -97,12 +111,12 @@ constructor(
educationShownCount = preferences[getEducationShownCountKey(gestureType)] ?: 0,
lastShortcutTriggeredTime =
preferences[getLastShortcutTriggeredTimeKey(gestureType)]?.let {
- Instant.ofEpochMilli(it)
+ Instant.ofEpochSecond(it)
},
)
}
- internal suspend fun updateGestureEduModel(
+ override suspend fun updateGestureEduModel(
gestureType: GestureType,
transform: (GestureEduModel) -> GestureEduModel
) {
@@ -134,7 +148,10 @@ constructor(
key: Preferences.Key<Long>
) {
if (instant != null) {
- preferences[key] = instant.toEpochMilli()
+ // Use epochSecond because an instant is defined as a signed long (64bit number) of
+ // seconds. Using toEpochMilli() on Instant.MIN or Instant.MAX will throw exception
+ // when converting to a long. So we use second instead of milliseconds for storage.
+ preferences[key] = instant.epochSecond
} else {
preferences.remove(key)
}
diff --git a/packages/SystemUI/src/com/android/systemui/education/domain/interactor/ContextualEducationInteractor.kt b/packages/SystemUI/src/com/android/systemui/education/domain/interactor/ContextualEducationInteractor.kt
index bee289d4b63a..5ec1006f8c42 100644
--- a/packages/SystemUI/src/com/android/systemui/education/domain/interactor/ContextualEducationInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/education/domain/interactor/ContextualEducationInteractor.kt
@@ -17,13 +17,15 @@
package com.android.systemui.education.domain.interactor
import com.android.systemui.CoreStartable
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.contextualeducation.GestureType
import com.android.systemui.contextualeducation.GestureType.BACK
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.education.dagger.ContextualEducationModule.EduClock
import com.android.systemui.education.data.model.GestureEduModel
import com.android.systemui.education.data.repository.ContextualEducationRepository
import com.android.systemui.user.domain.interactor.SelectedUserInteractor
+import java.time.Clock
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
@@ -43,6 +45,7 @@ class ContextualEducationInteractor
constructor(
@Background private val backgroundScope: CoroutineScope,
@Background private val backgroundDispatcher: CoroutineDispatcher,
+ @EduClock private val clock: Clock,
private val selectedUserInteractor: SelectedUserInteractor,
private val repository: ContextualEducationRepository,
) : CoreStartable {
@@ -64,9 +67,13 @@ constructor(
.flowOn(backgroundDispatcher)
}
- suspend fun incrementSignalCount(gestureType: GestureType) =
- repository.incrementSignalCount(gestureType)
+ suspend fun incrementSignalCount(gestureType: GestureType) {
+ repository.updateGestureEduModel(gestureType) { it.copy(signalCount = it.signalCount + 1) }
+ }
- suspend fun updateShortcutTriggerTime(gestureType: GestureType) =
- repository.updateShortcutTriggerTime(gestureType)
+ suspend fun updateShortcutTriggerTime(gestureType: GestureType) {
+ repository.updateGestureEduModel(gestureType) {
+ it.copy(lastShortcutTriggeredTime = clock.instant())
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/inputdevice/oobe/KeyboardTouchpadOobeTutorialCoreStartable.kt b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/KeyboardTouchpadTutorialCoreStartable.kt
index 701d3da1ee66..e8e1dd4c85d0 100644
--- a/packages/SystemUI/src/com/android/systemui/inputdevice/oobe/KeyboardTouchpadOobeTutorialCoreStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/KeyboardTouchpadTutorialCoreStartable.kt
@@ -14,23 +14,24 @@
* limitations under the License.
*/
-package com.android.systemui.inputdevice.oobe
+package com.android.systemui.inputdevice.tutorial
import com.android.systemui.CoreStartable
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.inputdevice.oobe.domain.interactor.OobeSchedulerInteractor
+import com.android.systemui.inputdevice.tutorial.domain.interactor.TutorialSchedulerInteractor
import com.android.systemui.shared.Flags.newTouchpadGesturesTutorial
import dagger.Lazy
import javax.inject.Inject
-/** A [CoreStartable] to launch a scheduler for keyboard and touchpad OOBE education */
+/** A [CoreStartable] to launch a scheduler for keyboard and touchpad education */
@SysUISingleton
-class KeyboardTouchpadOobeTutorialCoreStartable
+class KeyboardTouchpadTutorialCoreStartable
@Inject
-constructor(private val oobeSchedulerInteractor: Lazy<OobeSchedulerInteractor>) : CoreStartable {
+constructor(private val tutorialSchedulerInteractor: Lazy<TutorialSchedulerInteractor>) :
+ CoreStartable {
override fun start() {
if (newTouchpadGesturesTutorial()) {
- oobeSchedulerInteractor.get().start()
+ tutorialSchedulerInteractor.get().start()
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/inputdevice/oobe/data/model/OobeSchedulerInfo.kt b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/data/model/TutorialSchedulerInfo.kt
index e5aedc031ebe..cfe64e269c95 100644
--- a/packages/SystemUI/src/com/android/systemui/inputdevice/oobe/data/model/OobeSchedulerInfo.kt
+++ b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/data/model/TutorialSchedulerInfo.kt
@@ -14,14 +14,14 @@
* limitations under the License.
*/
-package com.android.systemui.inputdevice.oobe.data.model
+package com.android.systemui.inputdevice.tutorial.data.model
-data class OobeSchedulerInfo(
+data class TutorialSchedulerInfo(
val keyboard: DeviceSchedulerInfo = DeviceSchedulerInfo(),
val touchpad: DeviceSchedulerInfo = DeviceSchedulerInfo()
)
-data class DeviceSchedulerInfo(var isLaunched: Boolean = false, var connectionTime: Long? = null) {
+data class DeviceSchedulerInfo(var isLaunched: Boolean = false, var connectTime: Long? = null) {
val wasEverConnected: Boolean
- get() = connectionTime != null
+ get() = connectTime != null
}
diff --git a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/data/repository/TutorialSchedulerRepository.kt b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/data/repository/TutorialSchedulerRepository.kt
new file mode 100644
index 000000000000..31ff01836428
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/data/repository/TutorialSchedulerRepository.kt
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.inputdevice.tutorial.data.repository
+
+import android.content.Context
+import androidx.datastore.core.DataStore
+import androidx.datastore.preferences.core.Preferences
+import androidx.datastore.preferences.core.booleanPreferencesKey
+import androidx.datastore.preferences.core.edit
+import androidx.datastore.preferences.core.longPreferencesKey
+import androidx.datastore.preferences.preferencesDataStore
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.inputdevice.tutorial.data.model.DeviceSchedulerInfo
+import com.android.systemui.inputdevice.tutorial.data.model.TutorialSchedulerInfo
+import javax.inject.Inject
+import kotlinx.coroutines.flow.first
+import kotlinx.coroutines.flow.map
+
+@SysUISingleton
+class TutorialSchedulerRepository
+@Inject
+constructor(@Application private val applicationContext: Context) {
+
+ private val Context.dataStore: DataStore<Preferences> by
+ preferencesDataStore(name = DATASTORE_NAME)
+
+ suspend fun loadData(): TutorialSchedulerInfo {
+ return applicationContext.dataStore.data.map { pref -> getSchedulerInfo(pref) }.first()
+ }
+
+ suspend fun updateConnectTime(device: DeviceType, time: Long) {
+ applicationContext.dataStore.edit { pref -> pref[getConnectKey(device)] = time }
+ }
+
+ suspend fun updateLaunch(device: DeviceType) {
+ applicationContext.dataStore.edit { pref -> pref[getLaunchedKey(device)] = true }
+ }
+
+ private fun getSchedulerInfo(pref: Preferences): TutorialSchedulerInfo {
+ return TutorialSchedulerInfo(
+ keyboard = getDeviceSchedulerInfo(pref, DeviceType.KEYBOARD),
+ touchpad = getDeviceSchedulerInfo(pref, DeviceType.TOUCHPAD)
+ )
+ }
+
+ private fun getDeviceSchedulerInfo(pref: Preferences, device: DeviceType): DeviceSchedulerInfo {
+ val isLaunched = pref[getLaunchedKey(device)] ?: false
+ val connectionTime = pref[getConnectKey(device)] ?: null
+ return DeviceSchedulerInfo(isLaunched, connectionTime)
+ }
+
+ private fun getLaunchedKey(device: DeviceType) =
+ booleanPreferencesKey(device.name + IS_LAUNCHED_SUFFIX)
+
+ private fun getConnectKey(device: DeviceType) =
+ longPreferencesKey(device.name + CONNECT_TIME_SUFFIX)
+
+ companion object {
+ const val DATASTORE_NAME = "TutorialScheduler"
+ const val IS_LAUNCHED_SUFFIX = "_IS_LAUNCHED"
+ const val CONNECT_TIME_SUFFIX = "_CONNECTED_TIME"
+ }
+}
+
+enum class DeviceType {
+ KEYBOARD,
+ TOUCHPAD
+}
diff --git a/packages/SystemUI/src/com/android/systemui/inputdevice/oobe/domain/interactor/OobeSchedulerInteractor.kt b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/domain/interactor/TutorialSchedulerInteractor.kt
index b014c08d4564..05e104468f67 100644
--- a/packages/SystemUI/src/com/android/systemui/inputdevice/oobe/domain/interactor/OobeSchedulerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/domain/interactor/TutorialSchedulerInteractor.kt
@@ -14,14 +14,15 @@
* limitations under the License.
*/
-package com.android.systemui.inputdevice.oobe.domain.interactor
+package com.android.systemui.inputdevice.tutorial.domain.interactor
import android.content.Context
import android.content.Intent
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.inputdevice.oobe.data.model.DeviceSchedulerInfo
-import com.android.systemui.inputdevice.oobe.data.model.OobeSchedulerInfo
+import com.android.systemui.inputdevice.tutorial.data.model.DeviceSchedulerInfo
+import com.android.systemui.inputdevice.tutorial.data.repository.DeviceType
+import com.android.systemui.inputdevice.tutorial.data.repository.TutorialSchedulerRepository
import com.android.systemui.keyboard.data.repository.KeyboardRepository
import com.android.systemui.touchpad.data.repository.TouchpadRepository
import java.time.Duration
@@ -35,49 +36,65 @@ import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch
/**
- * When the first time a keyboard or touchpad id connected, wait for [LAUNCH_DELAY], then launch the
+ * When the first time a keyboard or touchpad is connected, wait for [LAUNCH_DELAY], then launch the
* tutorial as soon as there's a connected device
*/
@SysUISingleton
-class OobeSchedulerInteractor
+class TutorialSchedulerInteractor
@Inject
constructor(
@Application private val context: Context,
@Application private val applicationScope: CoroutineScope,
private val keyboardRepository: KeyboardRepository,
- private val touchpadRepository: TouchpadRepository
+ private val touchpadRepository: TouchpadRepository,
+ private val tutorialSchedulerRepository: TutorialSchedulerRepository
) {
- private val info = OobeSchedulerInfo()
-
fun start() {
- if (!info.keyboard.isLaunched) {
- applicationScope.launch {
- schedule(keyboardRepository.isAnyKeyboardConnected, info.keyboard)
+ applicationScope.launch {
+ val info = tutorialSchedulerRepository.loadData()
+ if (!info.keyboard.isLaunched) {
+ applicationScope.launch {
+ schedule(
+ keyboardRepository.isAnyKeyboardConnected,
+ info.keyboard,
+ DeviceType.KEYBOARD
+ )
+ }
}
- }
- if (!info.touchpad.isLaunched) {
- applicationScope.launch {
- schedule(touchpadRepository.isAnyTouchpadConnected, info.touchpad)
+ if (!info.touchpad.isLaunched) {
+ applicationScope.launch {
+ schedule(
+ touchpadRepository.isAnyTouchpadConnected,
+ info.touchpad,
+ DeviceType.TOUCHPAD
+ )
+ }
}
}
}
- private suspend fun schedule(isAnyDeviceConnected: Flow<Boolean>, info: DeviceSchedulerInfo) {
+ private suspend fun schedule(
+ isAnyDeviceConnected: Flow<Boolean>,
+ info: DeviceSchedulerInfo,
+ deviceType: DeviceType
+ ) {
if (!info.wasEverConnected) {
waitForDeviceConnection(isAnyDeviceConnected)
- info.connectionTime = Instant.now().toEpochMilli()
+ info.connectTime = Instant.now().toEpochMilli()
+ tutorialSchedulerRepository.updateConnectTime(deviceType, info.connectTime!!)
}
- delay(remainingTimeMillis(info.connectionTime!!))
+ delay(remainingTimeMillis(info.connectTime!!))
waitForDeviceConnection(isAnyDeviceConnected)
info.isLaunched = true
- launchOobe()
+ tutorialSchedulerRepository.updateLaunch(deviceType)
+ launchTutorial()
}
private suspend fun waitForDeviceConnection(isAnyDeviceConnected: Flow<Boolean>): Boolean {
return isAnyDeviceConnected.filter { it }.first()
}
- private fun launchOobe() {
+ private fun launchTutorial() {
val intent = Intent(TUTORIAL_ACTION)
intent.addCategory(Intent.CATEGORY_DEFAULT)
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
@@ -90,7 +107,6 @@ constructor(
}
companion object {
- const val TAG = "OobeSchedulerInteractor"
const val TUTORIAL_ACTION = "com.android.systemui.action.TOUCHPAD_TUTORIAL"
private val LAUNCH_DELAY = Duration.ofHours(72).toMillis()
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultNotificationStackScrollLayoutSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultNotificationStackScrollLayoutSection.kt
index 380e361eb33e..6ac33af26605 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultNotificationStackScrollLayoutSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultNotificationStackScrollLayoutSection.kt
@@ -24,7 +24,6 @@ import androidx.constraintlayout.widget.ConstraintSet.END
import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID
import androidx.constraintlayout.widget.ConstraintSet.START
import androidx.constraintlayout.widget.ConstraintSet.TOP
-import com.android.systemui.Flags.centralizedStatusBarHeightFix
import com.android.systemui.keyguard.MigrateClocksToBlueprint
import com.android.systemui.res.R
import com.android.systemui.shade.LargeScreenHeaderHelper
@@ -64,13 +63,7 @@ constructor(
val useLargeScreenHeader =
context.resources.getBoolean(R.bool.config_use_large_screen_shade_header)
val marginTopLargeScreen =
- if (centralizedStatusBarHeightFix()) {
- largeScreenHeaderHelperLazy.get().getLargeScreenHeaderHeight()
- } else {
- context.resources.getDimensionPixelSize(
- R.dimen.large_screen_shade_header_height
- )
- }
+ largeScreenHeaderHelperLazy.get().getLargeScreenHeaderHeight()
connect(
R.id.nssl_placeholder,
TOP,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
index db0676e26639..9939075b77d2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
@@ -18,8 +18,6 @@ package com.android.systemui.qs;
import static android.app.StatusBarManager.DISABLE2_QUICK_SETTINGS;
-import static com.android.systemui.Flags.centralizedStatusBarHeightFix;
-
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Path;
@@ -194,12 +192,7 @@ public class QSContainerImpl extends FrameLayout implements Dumpable {
QuickStatusBarHeaderController quickStatusBarHeaderController) {
int topPadding = QSUtils.getQsHeaderSystemIconsAreaHeight(mContext);
if (!LargeScreenUtils.shouldUseLargeScreenShadeHeader(mContext.getResources())) {
- topPadding =
- centralizedStatusBarHeightFix()
- ? LargeScreenHeaderHelper.getLargeScreenHeaderHeight(mContext)
- : mContext.getResources()
- .getDimensionPixelSize(
- R.dimen.large_screen_shade_header_height);
+ topPadding = LargeScreenHeaderHelper.getLargeScreenHeaderHeight(mContext);
}
if (mQSPanelContainer != null) {
mQSPanelContainer.setPaddingRelative(
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
index 9c88eb95c274..5a3f1c0b7426 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
@@ -17,8 +17,6 @@ package com.android.systemui.qs;
import static android.app.StatusBarManager.DISABLE2_QUICK_SETTINGS;
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
-import static com.android.systemui.Flags.centralizedStatusBarHeightFix;
-
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
@@ -100,10 +98,7 @@ public class QuickStatusBarHeader extends FrameLayout {
qqsLP.topMargin = mContext.getResources()
.getDimensionPixelSize(R.dimen.qqs_layout_margin_top);
} else {
- qqsLP.topMargin = centralizedStatusBarHeightFix()
- ? LargeScreenHeaderHelper.getLargeScreenHeaderHeight(mContext)
- : mContext.getResources()
- .getDimensionPixelSize(R.dimen.large_screen_shade_header_min_height);
+ qqsLP.topMargin = LargeScreenHeaderHelper.getLargeScreenHeaderHeight(mContext);
}
mHeaderQsPanel.setLayoutParams(qqsLP);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/IconTilesInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/IconTilesInteractor.kt
index b18358cedde7..6dcdea973d51 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/IconTilesInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/IconTilesInteractor.kt
@@ -40,15 +40,15 @@ constructor(
@Application private val applicationScope: CoroutineScope
) {
- private val largeTilesSpecs =
+ val largeTilesSpecs =
preferencesInteractor.largeTilesSpecs
.onEach { logChange(it) }
.stateIn(applicationScope, SharingStarted.Eagerly, repo.defaultLargeTiles)
fun isIconTile(spec: TileSpec): Boolean = !largeTilesSpecs.value.contains(spec)
- fun resize(spec: TileSpec, toIcon: Boolean) {
- if (toIcon) {
+ fun resize(spec: TileSpec) {
+ if (largeTilesSpecs.value.contains(spec)) {
preferencesInteractor.setLargeTilesSpecs(largeTilesSpecs.value - spec)
} else {
preferencesInteractor.setLargeTilesSpecs(largeTilesSpecs.value + spec)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/InfiniteGridConsistencyInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/InfiniteGridConsistencyInteractor.kt
index 0fe79af06a54..874b3b0a4636 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/InfiniteGridConsistencyInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/InfiniteGridConsistencyInteractor.kt
@@ -19,6 +19,7 @@ package com.android.systemui.qs.panels.domain.interactor
import android.util.Log
import com.android.systemui.dagger.SysUISingleton
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.TileRow
import com.android.systemui.qs.pipeline.shared.TileSpec
import javax.inject.Inject
@@ -38,17 +39,12 @@ constructor(
override fun reconcileTiles(tiles: List<TileSpec>): List<TileSpec> {
val newTiles: MutableList<TileSpec> = mutableListOf()
val row = TileRow<TileSpec>(columns = gridSizeInteractor.columns.value)
- val tilesQueue =
+ val tilesQueue: ArrayDeque<SizedTile<TileSpec>> =
ArrayDeque(
tiles.map {
- SizedTile(
+ SizedTileImpl(
it,
- width =
- if (iconTilesInteractor.isIconTile(it)) {
- 1
- } else {
- 2
- }
+ if (iconTilesInteractor.isIconTile(it)) 1 else 2,
)
}
)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/shared/model/TileRow.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/shared/model/TileRow.kt
index 7e4381bbff03..17b73a250524 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/shared/model/TileRow.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/shared/model/TileRow.kt
@@ -17,7 +17,17 @@
package com.android.systemui.qs.panels.shared.model
/** Represents a tile of type [T] associated with a width */
-data class SizedTile<T>(val tile: T, val width: Int)
+interface SizedTile<T> {
+ val tile: T
+ val width: Int
+ val isIcon: Boolean
+ get() = width == 1
+}
+
+data class SizedTileImpl<T>(
+ override val tile: T,
+ override val width: Int,
+) : SizedTile<T>
/** Represents a row of [SizedTile] with a maximum width of [columns] */
class TileRow<T>(private val columns: Int) {
@@ -51,3 +61,26 @@ class TileRow<T>(private val columns: Int) {
fun isFull(): Boolean = availableColumns == 0
}
+
+/**
+ * Converts a list of [SizedTile] to a sequence of rows based on the number of columns of the grid
+ */
+fun <T> splitInRowsSequence(
+ tiles: List<SizedTile<T>>,
+ columns: Int,
+): Sequence<List<SizedTile<T>>> = sequence {
+ val row = TileRow<T>(columns)
+ for (tile in tiles) {
+ check(tile.width <= columns)
+ if (!row.maybeAddTile(tile)) {
+ // Couldn't add tile to previous row, create a row with the current tiles
+ // and start a new one
+ yield(row.tiles)
+ row.clear()
+ row.maybeAddTile(tile)
+ }
+ }
+ if (row.tiles.isNotEmpty()) {
+ yield(row.tiles)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/DragAndDropState.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/DragAndDropState.kt
index 71deeb61b9e9..2c578130e920 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/DragAndDropState.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/DragAndDropState.kt
@@ -29,13 +29,14 @@ import androidx.compose.ui.draganddrop.DragAndDropEvent
import androidx.compose.ui.draganddrop.DragAndDropTarget
import androidx.compose.ui.draganddrop.DragAndDropTransferData
import androidx.compose.ui.draganddrop.mimeTypes
+import com.android.systemui.qs.panels.shared.model.SizedTile
import com.android.systemui.qs.panels.ui.viewmodel.EditTileViewModel
import com.android.systemui.qs.pipeline.shared.TileSpec
@Composable
fun rememberDragAndDropState(listState: EditTileListState): DragAndDropState {
- val sourceSpec: MutableState<EditTileViewModel?> = remember { mutableStateOf(null) }
- return remember(listState) { DragAndDropState(sourceSpec, listState) }
+ val draggedCell: MutableState<SizedTile<EditTileViewModel>?> = remember { mutableStateOf(null) }
+ return remember(listState) { DragAndDropState(draggedCell, listState) }
}
/**
@@ -43,37 +44,37 @@ fun rememberDragAndDropState(listState: EditTileListState): DragAndDropState {
* drop events.
*/
class DragAndDropState(
- val sourceSpec: MutableState<EditTileViewModel?>,
- private val listState: EditTileListState
+ val draggedCell: MutableState<SizedTile<EditTileViewModel>?>,
+ private val listState: EditTileListState,
) {
val dragInProgress: Boolean
- get() = sourceSpec.value != null
+ get() = draggedCell.value != null
/** Returns index of the dragged tile if it's present in the list. Returns -1 if not. */
fun currentPosition(): Int {
- return sourceSpec.value?.let { listState.indexOf(it.tileSpec) } ?: -1
+ return draggedCell.value?.let { listState.indexOf(it.tile.tileSpec) } ?: -1
}
fun isMoving(tileSpec: TileSpec): Boolean {
- return sourceSpec.value?.let { it.tileSpec == tileSpec } ?: false
+ return draggedCell.value?.let { it.tile.tileSpec == tileSpec } ?: false
}
- fun onStarted(tile: EditTileViewModel) {
- sourceSpec.value = tile
+ fun onStarted(cell: SizedTile<EditTileViewModel>) {
+ draggedCell.value = cell
}
fun onMoved(targetSpec: TileSpec) {
- sourceSpec.value?.let { listState.move(it, targetSpec) }
+ draggedCell.value?.let { listState.move(it, targetSpec) }
}
fun movedOutOfBounds() {
// Removing the tiles from the current tile grid if it moves out of bounds. This clears
// the spacer and makes it apparent that dropping the tile at that point would remove it.
- sourceSpec.value?.let { listState.remove(it.tileSpec) }
+ draggedCell.value?.let { listState.remove(it.tile.tileSpec) }
}
fun onDrop() {
- sourceSpec.value = null
+ draggedCell.value = null
}
}
@@ -97,8 +98,8 @@ fun Modifier.dragAndDropTile(
remember(dragAndDropState) {
object : DragAndDropTarget {
override fun onDrop(event: DragAndDropEvent): Boolean {
- return dragAndDropState.sourceSpec.value?.let {
- onDrop(it.tileSpec, dragAndDropState.currentPosition())
+ return dragAndDropState.draggedCell.value?.let {
+ onDrop(it.tile.tileSpec, dragAndDropState.currentPosition())
dragAndDropState.onDrop()
true
} ?: false
@@ -112,7 +113,7 @@ fun Modifier.dragAndDropTile(
return dragAndDropTarget(
shouldStartDragAndDrop = { event ->
event.mimeTypes().contains(QsDragAndDrop.TILESPEC_MIME_TYPE) &&
- dragAndDropState.sourceSpec.value?.let { acceptDrops(it.tileSpec) } ?: false
+ dragAndDropState.draggedCell.value?.let { acceptDrops(it.tile.tileSpec) } ?: false
},
target = target,
)
@@ -134,8 +135,8 @@ fun Modifier.dragAndDropRemoveZone(
remember(dragAndDropState) {
object : DragAndDropTarget {
override fun onDrop(event: DragAndDropEvent): Boolean {
- return dragAndDropState.sourceSpec.value?.let {
- onDrop(it.tileSpec)
+ return dragAndDropState.draggedCell.value?.let {
+ onDrop(it.tile.tileSpec)
dragAndDropState.onDrop()
true
} ?: false
@@ -176,8 +177,8 @@ fun Modifier.dragAndDropTileList(
}
override fun onDrop(event: DragAndDropEvent): Boolean {
- return dragAndDropState.sourceSpec.value?.let {
- onDrop(it.tileSpec, dragAndDropState.currentPosition())
+ return dragAndDropState.draggedCell.value?.let {
+ onDrop(it.tile.tileSpec, dragAndDropState.currentPosition())
dragAndDropState.onDrop()
true
} ?: false
@@ -188,23 +189,23 @@ fun Modifier.dragAndDropTileList(
target = target,
shouldStartDragAndDrop = { event ->
event.mimeTypes().contains(QsDragAndDrop.TILESPEC_MIME_TYPE) &&
- dragAndDropState.sourceSpec.value?.let { acceptDrops(it.tileSpec) } ?: false
+ dragAndDropState.draggedCell.value?.let { acceptDrops(it.tile.tileSpec) } ?: false
},
)
}
fun Modifier.dragAndDropTileSource(
- tile: EditTileViewModel,
+ sizedTile: SizedTile<EditTileViewModel>,
onTap: (TileSpec) -> Unit,
onDoubleTap: (TileSpec) -> Unit,
dragAndDropState: DragAndDropState
): Modifier {
return dragAndDropSource {
detectTapGestures(
- onTap = { onTap(tile.tileSpec) },
- onDoubleTap = { onDoubleTap(tile.tileSpec) },
+ onTap = { onTap(sizedTile.tile.tileSpec) },
+ onDoubleTap = { onDoubleTap(sizedTile.tile.tileSpec) },
onLongPress = {
- dragAndDropState.onStarted(tile)
+ dragAndDropState.onStarted(sizedTile)
// The tilespec from the ClipData transferred isn't actually needed as we're moving
// a tile within the same application. We're using a custom MIME type to limit the
@@ -214,7 +215,7 @@ fun Modifier.dragAndDropTileSource(
ClipData(
QsDragAndDrop.CLIPDATA_LABEL,
arrayOf(QsDragAndDrop.TILESPEC_MIME_TYPE),
- ClipData.Item(tile.tileSpec.spec)
+ ClipData.Item(sizedTile.tile.tileSpec.spec)
)
)
)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/EditTileListState.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/EditTileListState.kt
index e0fed2885799..fa3008e3f292 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/EditTileListState.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/EditTileListState.kt
@@ -20,22 +20,23 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.runtime.snapshots.SnapshotStateList
import androidx.compose.runtime.toMutableStateList
+import com.android.systemui.qs.panels.shared.model.SizedTile
import com.android.systemui.qs.panels.ui.viewmodel.EditTileViewModel
import com.android.systemui.qs.pipeline.shared.TileSpec
@Composable
fun rememberEditListState(
- tiles: List<EditTileViewModel>,
+ tiles: List<SizedTile<EditTileViewModel>>,
): EditTileListState {
return remember(tiles) { EditTileListState(tiles) }
}
/** Holds the temporary state of the tile list during a drag movement where we move tiles around. */
-class EditTileListState(tiles: List<EditTileViewModel>) {
- val tiles: SnapshotStateList<EditTileViewModel> = tiles.toMutableStateList()
+class EditTileListState(tiles: List<SizedTile<EditTileViewModel>>) {
+ val tiles: SnapshotStateList<SizedTile<EditTileViewModel>> = tiles.toMutableStateList()
- fun move(tile: EditTileViewModel, target: TileSpec) {
- val fromIndex = indexOf(tile.tileSpec)
+ fun move(sizedTile: SizedTile<EditTileViewModel>, target: TileSpec) {
+ val fromIndex = indexOf(sizedTile.tile.tileSpec)
val toIndex = indexOf(target)
if (toIndex == -1 || fromIndex == toIndex) {
@@ -44,7 +45,7 @@ class EditTileListState(tiles: List<EditTileViewModel>) {
if (fromIndex == -1) {
// If tile isn't in the list, simply insert it
- tiles.add(toIndex, tile)
+ tiles.add(toIndex, sizedTile)
} else {
// If tile is present in the list, move it
tiles.apply { add(toIndex, removeAt(fromIndex)) }
@@ -52,10 +53,10 @@ class EditTileListState(tiles: List<EditTileViewModel>) {
}
fun remove(tileSpec: TileSpec) {
- tiles.removeIf { it.tileSpec == tileSpec }
+ tiles.removeIf { it.tile.tileSpec == tileSpec }
}
fun indexOf(tileSpec: TileSpec): Int {
- return tiles.indexOfFirst { it.tileSpec == tileSpec }
+ return tiles.indexOfFirst { it.tile.tileSpec == tileSpec }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/InfiniteGridLayout.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/InfiniteGridLayout.kt
index add830e9760d..bd925fee2800 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/InfiniteGridLayout.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/InfiniteGridLayout.kt
@@ -22,12 +22,12 @@ import androidx.compose.foundation.lazy.grid.GridItemSpan
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.getValue
-import androidx.compose.runtime.rememberUpdatedState
+import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.dimensionResource
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.qs.panels.shared.model.SizedTile
+import com.android.systemui.qs.panels.shared.model.SizedTileImpl
import com.android.systemui.qs.panels.ui.viewmodel.EditTileViewModel
import com.android.systemui.qs.panels.ui.viewmodel.FixedColumnsSizeViewModel
import com.android.systemui.qs.panels.ui.viewmodel.IconTilesViewModel
@@ -56,13 +56,14 @@ constructor(
onDispose { tiles.forEach { it.stopListening(token) } }
}
val columns by gridSizeViewModel.columns.collectAsStateWithLifecycle()
+ val sizedTiles = tiles.map { SizedTileImpl(it, it.spec.width()) }
TileLazyGrid(modifier = modifier, columns = GridCells.Fixed(columns)) {
- items(tiles.size, span = { index -> GridItemSpan(tiles[index].spec.width()) }) { index
- ->
+ items(sizedTiles.size, span = { index -> GridItemSpan(sizedTiles[index].width) }) {
+ index ->
Tile(
- tile = tiles[index],
- iconOnly = iconTilesViewModel.isIconTile(tiles[index].spec),
+ tile = sizedTiles[index].tile,
+ iconOnly = iconTilesViewModel.isIconTile(sizedTiles[index].tile.spec),
modifier = Modifier.height(dimensionResource(id = R.dimen.qs_tile_height))
)
}
@@ -77,13 +78,21 @@ constructor(
onRemoveTile: (TileSpec) -> Unit,
) {
val columns by gridSizeViewModel.columns.collectAsStateWithLifecycle()
- val isIcon: (TileSpec) -> Boolean by rememberUpdatedState { tileSpec ->
- iconTilesViewModel.isIconTile(tileSpec)
- }
+ val largeTiles by iconTilesViewModel.largeTiles.collectAsStateWithLifecycle()
+
+ // Non-current tiles should always be displayed as icon tiles.
+ val sizedTiles =
+ remember(tiles, largeTiles) {
+ tiles.map {
+ SizedTileImpl(
+ it,
+ if (!it.isCurrent || !largeTiles.contains(it.tileSpec)) 1 else 2,
+ )
+ }
+ }
DefaultEditTileGrid(
- tiles = tiles,
- isIconOnly = isIcon,
+ sizedTiles = sizedTiles,
columns = columns,
modifier = modifier,
onAddTile = onAddTile,
@@ -99,7 +108,7 @@ constructor(
): List<List<TileViewModel>> {
return PaginatableGridLayout.splitInRows(
- tiles.map { SizedTile(it, it.spec.width()) },
+ tiles.map { SizedTileImpl(it, it.spec.width()) },
columns,
)
.chunked(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 9b4d10f27f9e..af3803b6ff34 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
@@ -52,7 +52,7 @@ fun QuickQuickSettings(
) { index ->
Tile(
tile = tiles[index],
- iconOnly = sizedTiles[index].width == 1,
+ iconOnly = sizedTiles[index].isIcon,
modifier = Modifier.height(dimensionResource(id = R.dimen.qs_tile_height))
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/Tile.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/Tile.kt
index cb9d0f6a790e..7e6ccd635a96 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/Tile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/Tile.kt
@@ -53,7 +53,6 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.foundation.lazy.grid.GridCells
-import androidx.compose.foundation.lazy.grid.GridItemSpan
import androidx.compose.foundation.lazy.grid.LazyGridScope
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
import androidx.compose.foundation.rememberScrollState
@@ -98,7 +97,8 @@ import com.android.systemui.common.ui.compose.Icon
import com.android.systemui.common.ui.compose.load
import com.android.systemui.plugins.qs.QSTile
import com.android.systemui.qs.panels.shared.model.SizedTile
-import com.android.systemui.qs.panels.shared.model.TileRow
+import com.android.systemui.qs.panels.ui.model.TileGridCell
+import com.android.systemui.qs.panels.ui.model.toTileGridCells
import com.android.systemui.qs.panels.ui.viewmodel.EditTileViewModel
import com.android.systemui.qs.panels.ui.viewmodel.TileViewModel
import com.android.systemui.qs.panels.ui.viewmodel.toUiState
@@ -107,12 +107,10 @@ import com.android.systemui.qs.pipeline.shared.TileSpec
import com.android.systemui.qs.tileimpl.QSTileImpl
import com.android.systemui.res.R
import java.util.function.Supplier
-import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.delay
object TileType
-@OptIn(ExperimentalCoroutinesApi::class)
@Composable
fun Tile(
tile: TileViewModel,
@@ -286,15 +284,14 @@ fun TileLazyGrid(
@Composable
fun DefaultEditTileGrid(
- tiles: List<EditTileViewModel>,
- isIconOnly: (TileSpec) -> Boolean,
+ sizedTiles: List<SizedTile<EditTileViewModel>>,
columns: Int,
modifier: Modifier,
onAddTile: (TileSpec, Int) -> Unit,
onRemoveTile: (TileSpec) -> Unit,
- onResize: (TileSpec, Boolean) -> Unit,
+ onResize: (TileSpec) -> Unit,
) {
- val (currentTiles, otherTiles) = tiles.partition { it.isCurrent }
+ val (currentTiles, otherTiles) = sizedTiles.partition { it.tile.isCurrent }
val currentListState = rememberEditListState(currentTiles)
val dragAndDropState = rememberDragAndDropState(currentListState)
@@ -304,9 +301,6 @@ fun DefaultEditTileGrid(
val onDropAdd: (TileSpec, Int) -> Unit by rememberUpdatedState { tileSpec, position ->
onAddTile(tileSpec, position)
}
- val onDoubleTap: (TileSpec) -> Unit by rememberUpdatedState { tileSpec ->
- onResize(tileSpec, !isIconOnly(tileSpec))
- }
val tilePadding = dimensionResource(R.dimen.qs_tile_margin_vertical)
CompositionLocalProvider(LocalOverscrollConfiguration provides null) {
@@ -332,9 +326,8 @@ fun DefaultEditTileGrid(
currentListState.tiles,
columns,
tilePadding,
- isIconOnly,
onRemoveTile,
- onDoubleTap,
+ onResize,
dragAndDropState,
onDropAdd,
)
@@ -422,48 +415,32 @@ private fun CurrentTilesContainer(content: @Composable () -> Unit) {
@Composable
private fun CurrentTilesGrid(
- tiles: List<EditTileViewModel>,
+ tiles: List<SizedTile<EditTileViewModel>>,
columns: Int,
tilePadding: Dp,
- isIconOnly: (TileSpec) -> Boolean,
onClick: (TileSpec) -> Unit,
- onDoubleTap: (TileSpec) -> Unit,
+ onResize: (TileSpec) -> Unit,
dragAndDropState: DragAndDropState,
onDrop: (TileSpec, Int) -> Unit
) {
- val tileHeight = tileHeight()
- val currentRows =
- remember(tiles) {
- calculateRows(
- tiles.map {
- SizedTile(
- it,
- if (isIconOnly(it.tileSpec)) {
- 1
- } else {
- 2
- }
- )
- },
- columns
- )
- }
- val currentGridHeight = gridHeight(currentRows, tileHeight, tilePadding)
// Current tiles
CurrentTilesContainer {
+ val cells = tiles.toTileGridCells(columns)
+ val tileHeight = tileHeight()
+ val totalRows = cells.lastOrNull()?.row ?: 0
+ val totalHeight = gridHeight(totalRows + 1, tileHeight, tilePadding)
TileLazyGrid(
modifier =
- Modifier.height(currentGridHeight)
+ Modifier.height(totalHeight)
.dragAndDropTileList(dragAndDropState, { true }, onDrop),
columns = GridCells.Fixed(columns)
) {
editTiles(
- tiles,
+ cells,
ClickAction.REMOVE,
onClick,
- isIconOnly,
dragAndDropState,
- onDoubleTap = onDoubleTap,
+ onResize = onResize,
indicatePosition = true,
acceptDrops = { true },
onDrop = onDrop,
@@ -474,13 +451,15 @@ private fun CurrentTilesGrid(
@Composable
private fun AvailableTileGrid(
- tiles: List<EditTileViewModel>,
+ tiles: List<SizedTile<EditTileViewModel>>,
columns: Int,
tilePadding: Dp,
onClick: (TileSpec) -> Unit,
dragAndDropState: DragAndDropState,
) {
- val (otherTilesStock, otherTilesCustom) = tiles.partition { it.appName == null }
+ // Available tiles aren't visible during drag and drop, so the row isn't needed
+ val (otherTilesStock, otherTilesCustom) =
+ tiles.map { TileGridCell(it, 0) }.partition { it.tile.appName == null }
val availableTileHeight = tileHeight(true)
val availableGridHeight = gridHeight(tiles.size, availableTileHeight, columns, tilePadding)
@@ -493,7 +472,6 @@ private fun AvailableTileGrid(
otherTilesStock,
ClickAction.ADD,
onClick,
- isIconOnly = { true },
dragAndDropState = dragAndDropState,
acceptDrops = { false },
showLabels = true,
@@ -502,7 +480,6 @@ private fun AvailableTileGrid(
otherTilesCustom,
ClickAction.ADD,
onClick,
- isIconOnly = { true },
dragAndDropState = dragAndDropState,
acceptDrops = { false },
showLabels = true,
@@ -519,52 +496,27 @@ fun gridHeight(rows: Int, tileHeight: Dp, padding: Dp): Dp {
return ((tileHeight + padding) * rows) - padding
}
-private fun calculateRows(tiles: List<SizedTile<EditTileViewModel>>, columns: Int): Int {
- val row = TileRow<EditTileViewModel>(columns)
- var count = 0
-
- for (tile in tiles) {
- if (row.maybeAddTile(tile)) {
- if (row.isFull()) {
- // Row is full, no need to stretch tiles
- count += 1
- row.clear()
- }
- } else {
- count += 1
- row.clear()
- row.maybeAddTile(tile)
- }
- }
- if (row.tiles.isNotEmpty()) {
- count += 1
- }
- return count
-}
-
fun LazyGridScope.editTiles(
- tiles: List<EditTileViewModel>,
+ cells: List<TileGridCell>,
clickAction: ClickAction,
onClick: (TileSpec) -> Unit,
- isIconOnly: (TileSpec) -> Boolean,
dragAndDropState: DragAndDropState,
acceptDrops: (TileSpec) -> Boolean,
- onDoubleTap: (TileSpec) -> Unit = {},
+ onResize: (TileSpec) -> Unit = {},
onDrop: (TileSpec, Int) -> Unit = { _, _ -> },
showLabels: Boolean = false,
indicatePosition: Boolean = false,
) {
items(
- count = tiles.size,
- key = { tiles[it].tileSpec.spec },
- span = { GridItemSpan(if (isIconOnly(tiles[it].tileSpec)) 1 else 2) },
+ count = cells.size,
+ key = { cells[it].key },
+ span = { cells[it].span },
contentType = { TileType }
) { index ->
- val viewModel = tiles[index]
- val iconOnly = isIconOnly(viewModel.tileSpec)
- val tileHeight = tileHeight(iconOnly && showLabels)
+ val cell = cells[index]
+ val tileHeight = tileHeight(cell.isIcon && showLabels)
- if (!dragAndDropState.isMoving(viewModel.tileSpec)) {
+ if (!dragAndDropState.isMoving(cell.tile.tileSpec)) {
val onClickActionName =
when (clickAction) {
ClickAction.ADD ->
@@ -579,8 +531,8 @@ fun LazyGridScope.editTiles(
""
}
EditTile(
- tileViewModel = viewModel,
- iconOnly = iconOnly,
+ tileViewModel = cell.tile,
+ iconOnly = cell.isIcon,
showLabels = showLabels,
modifier =
Modifier.height(tileHeight)
@@ -589,11 +541,11 @@ fun LazyGridScope.editTiles(
onClick(onClickActionName) { false }
this.stateDescription = stateDescription
}
- .dragAndDropTile(dragAndDropState, viewModel.tileSpec, acceptDrops, onDrop)
+ .dragAndDropTile(dragAndDropState, cell.tile.tileSpec, acceptDrops, onDrop)
.dragAndDropTileSource(
- viewModel,
+ cell,
onClick,
- onDoubleTap,
+ onResize,
dragAndDropState,
)
)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/model/TileGridCell.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/model/TileGridCell.kt
new file mode 100644
index 000000000000..c241fd87d9d5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/model/TileGridCell.kt
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.panels.ui.model
+
+import androidx.compose.foundation.lazy.grid.GridItemSpan
+import androidx.compose.runtime.Immutable
+import com.android.systemui.qs.panels.shared.model.SizedTile
+import com.android.systemui.qs.panels.shared.model.splitInRowsSequence
+import com.android.systemui.qs.panels.ui.viewmodel.EditTileViewModel
+
+/**
+ * Represents a [EditTileViewModel] from a grid associated with a tile format and the row it's
+ * positioned at
+ */
+@Immutable
+data class TileGridCell(
+ override val tile: EditTileViewModel,
+ val row: Int,
+ val key: String = "${tile.tileSpec.spec}-$row",
+ override val width: Int,
+) : SizedTile<EditTileViewModel> {
+ constructor(
+ sizedTile: SizedTile<EditTileViewModel>,
+ row: Int
+ ) : this(
+ tile = sizedTile.tile,
+ row = row,
+ width = sizedTile.width,
+ )
+
+ val span = GridItemSpan(width)
+}
+
+fun List<SizedTile<EditTileViewModel>>.toTileGridCells(columns: Int): List<TileGridCell> {
+ return splitInRowsSequence(this, columns)
+ .flatMapIndexed { index, sizedTiles -> sizedTiles.map { TileGridCell(it, index) } }
+ .toList()
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/IconTilesViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/IconTilesViewModel.kt
index 8d2d74af5835..b604e18b1e76 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/IconTilesViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/IconTilesViewModel.kt
@@ -20,17 +20,22 @@ import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.qs.panels.domain.interactor.IconTilesInteractor
import com.android.systemui.qs.pipeline.shared.TileSpec
import javax.inject.Inject
+import kotlinx.coroutines.flow.StateFlow
interface IconTilesViewModel {
+ val largeTiles: StateFlow<Set<TileSpec>>
+
fun isIconTile(spec: TileSpec): Boolean
- fun resize(spec: TileSpec, toIcon: Boolean)
+ fun resize(spec: TileSpec)
}
@SysUISingleton
class IconTilesViewModelImpl @Inject constructor(private val interactor: IconTilesInteractor) :
IconTilesViewModel {
+ override val largeTiles = interactor.largeTilesSpecs
+
override fun isIconTile(spec: TileSpec): Boolean = interactor.isIconTile(spec)
- override fun resize(spec: TileSpec, toIcon: Boolean) = interactor.resize(spec, toIcon)
+ override fun resize(spec: TileSpec) = interactor.resize(spec)
}
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 bb004946a4d1..eee905f9f894 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
@@ -20,7 +20,8 @@ import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
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.TileRow
+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
@@ -59,7 +60,12 @@ constructor(
.flatMapLatest { columns ->
tilesInteractor.currentTiles.combine(rows, ::Pair).mapLatest { (tiles, rows) ->
tiles
- .map { SizedTile(TileViewModel(it.tile, it.spec), it.spec.width) }
+ .map {
+ SizedTileImpl(
+ TileViewModel(it.tile, it.spec),
+ it.spec.width,
+ )
+ }
.let { splitInRowsSequence(it, columns).take(rows).toList().flatten() }
}
}
@@ -67,7 +73,12 @@ constructor(
applicationScope,
SharingStarted.WhileSubscribed(),
tilesInteractor.currentTiles.value
- .map { SizedTile(TileViewModel(it.tile, it.spec), it.spec.width) }
+ .map {
+ SizedTileImpl(
+ TileViewModel(it.tile, it.spec),
+ it.spec.width,
+ )
+ }
.let {
splitInRowsSequence(it, columns.value).take(rows.value).toList().flatten()
}
@@ -75,26 +86,4 @@ constructor(
private val TileSpec.width: Int
get() = if (iconTilesViewModel.isIconTile(this)) 1 else 2
-
- companion object {
- private fun splitInRowsSequence(
- tiles: List<SizedTile<TileViewModel>>,
- columns: Int,
- ): Sequence<List<SizedTile<TileViewModel>>> = sequence {
- val row = TileRow<TileViewModel>(columns)
- for (tile in tiles) {
- check(tile.width <= columns)
- if (!row.maybeAddTile(tile)) {
- // Couldn't add tile to previous row, create a row with the current tiles
- // and start a new one
- yield(row.tiles)
- row.clear()
- row.maybeAddTile(tile)
- }
- }
- if (row.tiles.isNotEmpty()) {
- yield(row.tiles)
- }
- }
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
index cbb61b37b7a4..117035422c51 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
@@ -181,7 +181,7 @@ public class RecordingService extends Service implements ScreenMediaRecorderList
mUiEventLogger.log(Events.ScreenRecordEvent.SCREEN_RECORD_START);
} else {
updateState(false);
- createErrorNotification();
+ createErrorStartingNotification();
stopForeground(STOP_FOREGROUND_DETACH);
stopSelf();
return Service.START_NOT_STICKY;
@@ -272,17 +272,30 @@ public class RecordingService extends Service implements ScreenMediaRecorderList
}
/**
- * Simple error notification, needed since startForeground must be called to avoid errors
+ * Simple "error starting" notification, needed since startForeground must be called to avoid
+ * errors.
*/
@VisibleForTesting
- protected void createErrorNotification() {
+ protected void createErrorStartingNotification() {
+ createErrorNotification(strings().getStartError());
+ }
+
+ /**
+ * Simple "error saving" notification, needed since startForeground must be called to avoid
+ * errors.
+ */
+ @VisibleForTesting
+ protected void createErrorSavingNotification() {
+ createErrorNotification(strings().getSaveError());
+ }
+
+ private void createErrorNotification(String notificationContentTitle) {
Bundle extras = new Bundle();
extras.putString(Notification.EXTRA_SUBSTITUTE_APP_NAME, strings().getTitle());
- String notificationTitle = strings().getStartError();
Notification.Builder builder = new Notification.Builder(this, getChannelId())
.setSmallIcon(R.drawable.ic_screenrecord)
- .setContentTitle(notificationTitle)
+ .setContentTitle(notificationContentTitle)
.addExtras(extras);
startForeground(mNotificationId, builder.build());
}
@@ -427,11 +440,11 @@ public class RecordingService extends Service implements ScreenMediaRecorderList
// let's release the recorder and delete all temporary files in this case
getRecorder().release();
}
- showErrorToast(R.string.screenrecord_start_error);
+ showErrorToast(R.string.screenrecord_save_error);
Log.e(getTag(), "stopRecording called, but there was an error when ending"
+ "recording");
exception.printStackTrace();
- createErrorNotification();
+ createErrorSavingNotification();
} catch (Throwable throwable) {
if (getRecorder() != null) {
// Something unexpected happen, SystemUI will crash but let's delete
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt b/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt
index 8b88da1754f0..348b6bab1617 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt
@@ -27,7 +27,6 @@ import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID
import androidx.constraintlayout.widget.ConstraintSet.START
import androidx.constraintlayout.widget.ConstraintSet.TOP
import androidx.lifecycle.lifecycleScope
-import com.android.systemui.Flags.centralizedStatusBarHeightFix
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.fragments.FragmentService
@@ -191,11 +190,7 @@ constructor(
}
private fun calculateLargeShadeHeaderHeight(): Int {
- return if (centralizedStatusBarHeightFix()) {
- largeScreenHeaderHelperLazy.get().getLargeScreenHeaderHeight()
- } else {
- resources.getDimensionPixelSize(R.dimen.large_screen_shade_header_height)
- }
+ return largeScreenHeaderHelperLazy.get().getLargeScreenHeaderHeight()
}
private fun calculateShadeHeaderHeight(): Int {
diff --git a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsControllerImpl.java b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsControllerImpl.java
index 9f61d4e5d949..0a092a088e69 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsControllerImpl.java
@@ -19,7 +19,6 @@ package com.android.systemui.shade;
import static android.view.WindowInsets.Type.ime;
-import static com.android.systemui.Flags.centralizedStatusBarHeightFix;
import static com.android.systemui.classifier.Classifier.QS_COLLAPSE;
import static com.android.systemui.shade.NotificationPanelViewController.COUNTER_PANEL_OPEN_QS;
import static com.android.systemui.shade.NotificationPanelViewController.FLING_COLLAPSE;
@@ -444,10 +443,7 @@ public class QuickSettingsControllerImpl implements QuickSettingsController, Dum
mUseLargeScreenShadeHeader =
LargeScreenUtils.shouldUseLargeScreenShadeHeader(mPanelView.getResources());
mLargeScreenShadeHeaderHeight =
- centralizedStatusBarHeightFix()
- ? mLargeScreenHeaderHelperLazy.get().getLargeScreenHeaderHeight()
- : mResources.getDimensionPixelSize(
- R.dimen.large_screen_shade_header_height);
+ mLargeScreenHeaderHelperLazy.get().getLargeScreenHeaderHeight();
int topMargin = mUseLargeScreenShadeHeader ? mLargeScreenShadeHeaderHeight :
mResources.getDimensionPixelSize(R.dimen.notification_panel_margin_top);
mShadeHeaderController.setLargeScreenActive(mUseLargeScreenShadeHeader);
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt
index 37da114137fe..c49cfbde25a5 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt
@@ -38,7 +38,6 @@ import androidx.core.view.doOnLayout
import com.android.app.animation.Interpolators
import com.android.settingslib.Utils
import com.android.systemui.Dumpable
-import com.android.systemui.Flags.centralizedStatusBarHeightFix
import com.android.systemui.animation.ShadeInterpolation
import com.android.systemui.battery.BatteryMeterView
import com.android.systemui.battery.BatteryMeterViewController
@@ -231,10 +230,12 @@ constructor(
private val demoModeReceiver =
object : DemoMode {
override fun demoCommands() = listOf(DemoMode.COMMAND_CLOCK)
+
override fun dispatchDemoCommand(command: String, args: Bundle) =
clock.dispatchDemoCommand(command, args)
override fun onDemoModeStarted() = clock.onDemoModeStarted()
+
override fun onDemoModeFinished() = clock.onDemoModeFinished()
}
@@ -442,9 +443,7 @@ constructor(
changes += combinedShadeHeadersConstraintManager.emptyCutoutConstraints()
}
- if (centralizedStatusBarHeightFix()) {
- view.setPadding(view.paddingLeft, sbInsets.top, view.paddingRight, view.paddingBottom)
- }
+ view.setPadding(view.paddingLeft, sbInsets.top, view.paddingRight, view.paddingBottom)
view.updateAllConstraints(changes)
updateBatteryMode()
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/NotificationShadeWindowModel.kt b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/NotificationShadeWindowModel.kt
index e1289af58f06..2f9848863059 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/NotificationShadeWindowModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/NotificationShadeWindowModel.kt
@@ -17,8 +17,11 @@
package com.android.systemui.shade.ui.viewmodel
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING
import com.android.systemui.keyguard.shared.model.KeyguardState.OCCLUDED
+import com.android.systemui.util.kotlin.BooleanFlowOperators.anyOf
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
@@ -29,7 +32,11 @@ class NotificationShadeWindowModel
@Inject
constructor(
keyguardTransitionInteractor: KeyguardTransitionInteractor,
+ keyguardInteractor: KeyguardInteractor,
) {
val isKeyguardOccluded: Flow<Boolean> =
- keyguardTransitionInteractor.transitionValue(OCCLUDED).map { it == 1f }
+ anyOf(
+ keyguardTransitionInteractor.transitionValue(OCCLUDED).map { it == 1f },
+ keyguardTransitionInteractor.transitionValue(DREAMING).map { it == 1f },
+ )
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/ui/viewmodel/ScreenRecordChipViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/ui/viewmodel/ScreenRecordChipViewModel.kt
index 6ba4fefd6f3c..9e6cacb8b9ff 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/ui/viewmodel/ScreenRecordChipViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/ui/viewmodel/ScreenRecordChipViewModel.kt
@@ -41,6 +41,7 @@ import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
import com.android.systemui.statusbar.chips.ui.viewmodel.ChipTransitionHelper
import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipViewModel
import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipViewModel.Companion.createDialogLaunchOnClickListener
+import com.android.systemui.util.kotlin.pairwise
import com.android.systemui.util.time.SystemClock
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
@@ -64,7 +65,8 @@ constructor(
@StatusBarChipsLog private val logger: LogBuffer,
) : OngoingActivityChipViewModel {
- private val internalChip =
+ /** A direct mapping from [ScreenRecordChipModel] to [OngoingActivityChipModel]. */
+ private val simpleChip =
interactor.screenRecordState
.map { state ->
when (state) {
@@ -105,10 +107,31 @@ constructor(
// See b/347726238 for [SharingStarted.Lazily] reasoning.
.stateIn(scope, SharingStarted.Lazily, OngoingActivityChipModel.Hidden())
+ /**
+ * The screen record chip to show that also ensures that the start time doesn't change once we
+ * enter the recording state. If we change the start time while we're recording, the chronometer
+ * could skip a second. See b/349620526.
+ */
+ private val chipWithConsistentTimer: StateFlow<OngoingActivityChipModel> =
+ simpleChip
+ .pairwise(initialValue = OngoingActivityChipModel.Hidden())
+ .map { (old, new) ->
+ if (
+ old is OngoingActivityChipModel.Shown.Timer &&
+ new is OngoingActivityChipModel.Shown.Timer
+ ) {
+ new.copy(startTimeMs = old.startTimeMs)
+ } else {
+ new
+ }
+ }
+ // See b/347726238 for [SharingStarted.Lazily] reasoning.
+ .stateIn(scope, SharingStarted.Lazily, OngoingActivityChipModel.Hidden())
+
private val chipTransitionHelper = ChipTransitionHelper(scope)
override val chip: StateFlow<OngoingActivityChipModel> =
- chipTransitionHelper.createChipFlow(internalChip)
+ chipTransitionHelper.createChipFlow(chipWithConsistentTimer)
private fun createDelegate(
recordedTask: ActivityManager.RunningTaskInfo?
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/ColorsModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/ColorsModel.kt
index 130b1170c9f1..8a5165d8df7b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/ColorsModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/ColorsModel.kt
@@ -18,7 +18,6 @@ package com.android.systemui.statusbar.chips.ui.model
import android.content.Context
import android.content.res.ColorStateList
-import android.view.ContextThemeWrapper
import androidx.annotation.ColorInt
import com.android.settingslib.Utils
import com.android.systemui.res.R
@@ -43,9 +42,7 @@ sealed interface ColorsModel {
/** The chip should have a red background with white text. */
data object Red : ColorsModel {
override fun background(context: Context): ColorStateList {
- val themedContext =
- ContextThemeWrapper(context, com.android.internal.R.style.Theme_DeviceDefault_Light)
- return Utils.getColorAttr(themedContext, com.android.internal.R.attr.materialColorError)
+ return ColorStateList.valueOf(context.getColor(R.color.GM2_red_700))
}
override fun text(context: Context) = context.getColor(android.R.color.white)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractor.kt
index 9b21fa9bbe35..2537affbb28b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractor.kt
@@ -18,7 +18,6 @@
package com.android.systemui.statusbar.notification.stack.domain.interactor
import android.content.Context
-import com.android.systemui.Flags.centralizedStatusBarHeightFix
import com.android.systemui.common.ui.data.repository.ConfigurationRepository
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor
@@ -75,11 +74,7 @@ constructor(
getDimensionPixelSize(R.dimen.notification_panel_margin_bottom),
marginTop = getDimensionPixelSize(R.dimen.notification_panel_margin_top),
marginTopLargeScreen =
- if (centralizedStatusBarHeightFix()) {
- largeScreenHeaderHelperLazy.get().getLargeScreenHeaderHeight()
- } else {
- getDimensionPixelSize(R.dimen.large_screen_shade_header_height)
- },
+ largeScreenHeaderHelperLazy.get().getLargeScreenHeaderHeight(),
keyguardSplitShadeTopMargin =
getDimensionPixelSize(R.dimen.keyguard_split_shade_top_margin),
)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
index 2c7ce00adbeb..b6d58d6a23d9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -2269,7 +2269,10 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
// applying the dimming effect twice.
mUiBgExecutor.execute(() -> {
float dimAmount = 0f;
- if (mWallpaperManager.lockScreenWallpaperExists()) {
+ // Note that access to WallpaperManager APIs should be guarded by a check into
+ // WallpaperManager#isWallpaperSupported. Form factors that do not use wallpaper
+ // may crash SysUI during improper access. ref: b/355307617
+ if (!mWallpaperSupported || mWallpaperManager.lockScreenWallpaperExists()) {
dimAmount = mWallpaperManager.getWallpaperDimAmount();
}
final float scrimDimAmount = dimAmount;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
index 0adc1b0af66f..013903a5eb3d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
@@ -16,7 +16,6 @@
package com.android.systemui.statusbar.phone;
-import static com.android.systemui.Flags.centralizedStatusBarHeightFix;
import static com.android.systemui.doze.util.BurnInHelperKt.getBurnInOffset;
import static com.android.systemui.doze.util.BurnInHelperKt.getBurnInScale;
import static com.android.systemui.statusbar.notification.NotificationUtils.interpolate;
@@ -169,9 +168,7 @@ public class KeyguardClockPositionAlgorithm {
mStatusViewBottomMargin =
res.getDimensionPixelSize(R.dimen.keyguard_status_view_bottom_margin);
mSplitShadeTopNotificationsMargin =
- centralizedStatusBarHeightFix()
- ? LargeScreenHeaderHelper.getLargeScreenHeaderHeight(context)
- : res.getDimensionPixelSize(R.dimen.large_screen_shade_header_height);
+ LargeScreenHeaderHelper.getLargeScreenHeaderHeight(context);
mSplitShadeTargetTopMargin =
res.getDimensionPixelSize(R.dimen.keyguard_split_shade_top_margin);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
index 84e601848b91..c3da7fcc86c8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
@@ -16,7 +16,6 @@
package com.android.systemui.statusbar.phone;
-import static com.android.systemui.Flags.centralizedStatusBarHeightFix;
import static com.android.systemui.ScreenDecorations.DisplayCutoutView.boundsFromDirection;
import static com.android.systemui.util.Utils.getStatusBarHeaderHeightKeyguard;
@@ -133,9 +132,6 @@ public class KeyguardStatusBarView extends RelativeLayout {
mUserSwitcherContainer = findViewById(R.id.user_switcher_container);
mIsPrivacyDotEnabled = mContext.getResources().getBoolean(R.bool.config_enablePrivacyDot);
loadDimens();
- if (!centralizedStatusBarHeightFix()) {
- setGravity(Gravity.CENTER_VERTICAL);
- }
}
/**
@@ -322,7 +318,7 @@ public class KeyguardStatusBarView extends RelativeLayout {
final int minRight = (!isLayoutRtl() && mIsPrivacyDotEnabled)
? Math.max(mMinDotWidth, mPadding.right) : mPadding.right;
- int top = centralizedStatusBarHeightFix() ? waterfallTop + mPadding.top : waterfallTop;
+ int top = waterfallTop + mPadding.top;
setPadding(minLeft, top, minRight, 0);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java
index f866f740345e..79c206c1a838 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java
@@ -126,7 +126,8 @@ public class RecordingServiceTest extends SysuiTestCase {
doNothing().when(mRecordingService).createRecordingNotification();
doReturn(mNotification).when(mRecordingService).createProcessingNotification();
doReturn(mNotification).when(mRecordingService).createSaveNotification(any());
- doNothing().when(mRecordingService).createErrorNotification();
+ doNothing().when(mRecordingService).createErrorStartingNotification();
+ doNothing().when(mRecordingService).createErrorSavingNotification();
doNothing().when(mRecordingService).showErrorToast(anyInt());
doNothing().when(mRecordingService).stopForeground(anyInt());
@@ -234,7 +235,7 @@ public class RecordingServiceTest extends SysuiTestCase {
mRecordingService.onStopped();
- verify(mRecordingService).createErrorNotification();
+ verify(mRecordingService).createErrorSavingNotification();
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerLegacyTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerLegacyTest.kt
index 2c2fcbe75e1b..13bc82fa2c70 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerLegacyTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerLegacyTest.kt
@@ -17,7 +17,6 @@
package com.android.systemui.shade
import android.platform.test.annotations.DisableFlags
-import android.platform.test.annotations.EnableFlags
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.view.View
@@ -28,7 +27,6 @@ import androidx.annotation.IdRes
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.constraintlayout.widget.ConstraintSet
import androidx.test.filters.SmallTest
-import com.android.systemui.Flags.FLAG_CENTRALIZED_STATUS_BAR_HEIGHT_FIX
import com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT
import com.android.systemui.SysuiTestCase
import com.android.systemui.fragments.FragmentHostManager
@@ -166,31 +164,7 @@ class NotificationsQSContainerControllerLegacyTest : SysuiTestCase() {
}
@Test
- @DisableFlags(FLAG_CENTRALIZED_STATUS_BAR_HEIGHT_FIX)
- fun testLargeScreen_updateResources_refactorFlagOff_splitShadeHeightIsSetBasedOnResource() {
- val headerResourceHeight = 20
- val headerHelperHeight = 30
- whenever(largeScreenHeaderHelper.getLargeScreenHeaderHeight())
- .thenReturn(headerHelperHeight)
- overrideResource(R.bool.config_use_large_screen_shade_header, true)
- overrideResource(R.dimen.qs_header_height, 10)
- overrideResource(R.dimen.large_screen_shade_header_height, headerResourceHeight)
-
- // ensure the estimated height (would be 3 here) wouldn't impact this test case
- overrideResource(R.dimen.large_screen_shade_header_min_height, 1)
- overrideResource(R.dimen.new_qs_header_non_clickable_element_height, 1)
-
- underTest.updateResources()
-
- val captor = ArgumentCaptor.forClass(ConstraintSet::class.java)
- verify(view).applyConstraints(capture(captor))
- assertThat(captor.value.getHeight(R.id.split_shade_status_bar))
- .isEqualTo(headerResourceHeight)
- }
-
- @Test
- @EnableFlags(FLAG_CENTRALIZED_STATUS_BAR_HEIGHT_FIX)
- fun testLargeScreen_updateResources_refactorFlagOn_splitShadeHeightIsSetBasedOnHelper() {
+ fun testLargeScreen_updateResources_splitShadeHeightIsSetBasedOnHelper() {
val headerResourceHeight = 20
val headerHelperHeight = 30
whenever(largeScreenHeaderHelper.getLargeScreenHeaderHeight())
@@ -447,31 +421,8 @@ class NotificationsQSContainerControllerLegacyTest : SysuiTestCase() {
}
@Test
- @DisableFlags(FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT, FLAG_CENTRALIZED_STATUS_BAR_HEIGHT_FIX)
- fun testLargeScreenLayout_refactorFlagOff_qsAndNotifsTopMarginIsOfHeaderHeightResource() {
- setLargeScreen()
- val largeScreenHeaderResourceHeight = 100
- val largeScreenHeaderHelperHeight = 200
- whenever(largeScreenHeaderHelper.getLargeScreenHeaderHeight())
- .thenReturn(largeScreenHeaderHelperHeight)
- overrideResource(R.dimen.large_screen_shade_header_height, largeScreenHeaderResourceHeight)
-
- // ensure the estimated height (would be 30 here) wouldn't impact this test case
- overrideResource(R.dimen.large_screen_shade_header_min_height, 10)
- overrideResource(R.dimen.new_qs_header_non_clickable_element_height, 10)
-
- underTest.updateResources()
-
- assertThat(getConstraintSetLayout(R.id.qs_frame).topMargin)
- .isEqualTo(largeScreenHeaderResourceHeight)
- assertThat(getConstraintSetLayout(R.id.notification_stack_scroller).topMargin)
- .isEqualTo(largeScreenHeaderResourceHeight)
- }
-
- @Test
@DisableFlags(FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
- @EnableFlags(FLAG_CENTRALIZED_STATUS_BAR_HEIGHT_FIX)
- fun testLargeScreenLayout_refactorFlagOn_qsAndNotifsTopMarginIsOfHeaderHeightHelper() {
+ fun testLargeScreenLayout_qsAndNotifsTopMarginIsOfHeaderHeightHelper() {
setLargeScreen()
val largeScreenHeaderResourceHeight = 100
val largeScreenHeaderHelperHeight = 200
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerTest.kt
index f21def361e40..4850b0f67857 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerTest.kt
@@ -16,7 +16,6 @@
package com.android.systemui.shade
-import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
@@ -28,7 +27,6 @@ import androidx.annotation.IdRes
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.constraintlayout.widget.ConstraintSet
import androidx.test.filters.SmallTest
-import com.android.systemui.Flags.FLAG_CENTRALIZED_STATUS_BAR_HEIGHT_FIX
import com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT
import com.android.systemui.SysuiTestCase
import com.android.systemui.fragments.FragmentHostManager
@@ -164,29 +162,7 @@ class NotificationsQSContainerControllerTest : SysuiTestCase() {
}
@Test
- @DisableFlags(FLAG_CENTRALIZED_STATUS_BAR_HEIGHT_FIX)
- fun testLargeScreen_updateResources_refactorFlagOff_splitShadeHeightIsSet_basedOnResource() {
- val helperHeight = 30
- val resourceHeight = 20
- whenever(largeScreenHeaderHelper.getLargeScreenHeaderHeight()).thenReturn(helperHeight)
- overrideResource(R.bool.config_use_large_screen_shade_header, true)
- overrideResource(R.dimen.qs_header_height, 10)
- overrideResource(R.dimen.large_screen_shade_header_height, resourceHeight)
-
- // ensure the estimated height (would be 3 here) wouldn't impact this test case
- overrideResource(R.dimen.large_screen_shade_header_min_height, 1)
- overrideResource(R.dimen.new_qs_header_non_clickable_element_height, 1)
-
- underTest.updateResources()
-
- val captor = ArgumentCaptor.forClass(ConstraintSet::class.java)
- verify(view).applyConstraints(capture(captor))
- assertThat(captor.value.getHeight(R.id.split_shade_status_bar)).isEqualTo(resourceHeight)
- }
-
- @Test
- @EnableFlags(FLAG_CENTRALIZED_STATUS_BAR_HEIGHT_FIX)
- fun testLargeScreen_updateResources_refactorFlagOn_splitShadeHeightIsSet_basedOnHelper() {
+ fun testLargeScreen_updateResources_splitShadeHeightIsSet_basedOnHelper() {
val helperHeight = 30
val resourceHeight = 20
whenever(largeScreenHeaderHelper.getLargeScreenHeaderHeight()).thenReturn(helperHeight)
@@ -427,28 +403,7 @@ class NotificationsQSContainerControllerTest : SysuiTestCase() {
}
@Test
- @DisableFlags(FLAG_CENTRALIZED_STATUS_BAR_HEIGHT_FIX)
- fun testLargeScreenLayout_refactorFlagOff_qsAndNotifsTopMarginIsOfHeaderResourceHeight() {
- setLargeScreen()
- val largeScreenHeaderHelperHeight = 200
- val largeScreenHeaderResourceHeight = 100
- whenever(largeScreenHeaderHelper.getLargeScreenHeaderHeight())
- .thenReturn(largeScreenHeaderHelperHeight)
- overrideResource(R.dimen.large_screen_shade_header_height, largeScreenHeaderResourceHeight)
-
- // ensure the estimated height (would be 30 here) wouldn't impact this test case
- overrideResource(R.dimen.large_screen_shade_header_min_height, 10)
- overrideResource(R.dimen.new_qs_header_non_clickable_element_height, 10)
-
- underTest.updateResources()
-
- assertThat(getConstraintSetLayout(R.id.qs_frame).topMargin)
- .isEqualTo(largeScreenHeaderResourceHeight)
- }
-
- @Test
- @EnableFlags(FLAG_CENTRALIZED_STATUS_BAR_HEIGHT_FIX)
- fun testLargeScreenLayout_refactorFlagOn_qsAndNotifsTopMarginIsOfHeaderHelperHeight() {
+ fun testLargeScreenLayout_qsAndNotifsTopMarginIsOfHeaderHelperHeight() {
setLargeScreen()
val largeScreenHeaderHelperHeight = 200
val largeScreenHeaderResourceHeight = 100
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/screenrecord/ui/viewmodel/ScreenRecordChipViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/screenrecord/ui/viewmodel/ScreenRecordChipViewModelTest.kt
index e68fa0bc6eb3..804eb5cf597c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/screenrecord/ui/viewmodel/ScreenRecordChipViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/screenrecord/ui/viewmodel/ScreenRecordChipViewModelTest.kt
@@ -231,6 +231,34 @@ class ScreenRecordChipViewModelTest : SysuiTestCase() {
assertThat((latest as OngoingActivityChipModel.Shown.Timer).startTimeMs).isEqualTo(5678)
}
+ /** Regression test for b/349620526. */
+ @Test
+ fun chip_recordingState_thenGetsTaskInfo_startTimeDoesNotChange() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.chip)
+
+ // Start recording, but without any task info
+ systemClock.setElapsedRealtime(1234)
+ screenRecordRepo.screenRecordState.value = ScreenRecordModel.Recording
+ mediaProjectionRepo.mediaProjectionState.value = MediaProjectionState.NotProjecting
+
+ assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java)
+ assertThat((latest as OngoingActivityChipModel.Shown.Timer).startTimeMs).isEqualTo(1234)
+
+ // WHEN we receive the recording task info a few milliseconds later
+ systemClock.setElapsedRealtime(1240)
+ mediaProjectionRepo.mediaProjectionState.value =
+ MediaProjectionState.Projecting.SingleTask(
+ "host.package",
+ hostDeviceName = null,
+ FakeActivityTaskManager.createTask(taskId = 1)
+ )
+
+ // THEN the start time is still the old start time
+ assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java)
+ assertThat((latest as OngoingActivityChipModel.Shown.Timer).startTimeMs).isEqualTo(1234)
+ }
+
@Test
fun chip_notProjecting_clickListenerShowsDialog() =
testScope.runTest {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java
index 7f33c23e8abc..eb1e28b891f7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java
@@ -26,13 +26,10 @@ import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.when;
import android.content.res.Resources;
-import android.platform.test.annotations.DisableFlags;
-import android.platform.test.annotations.EnableFlags;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
-import com.android.systemui.Flags;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.doze.util.BurnInHelperKt;
import com.android.systemui.log.LogBuffer;
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/education/data/repository/ContextualEducationRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/education/data/repository/ContextualEducationRepositoryKosmos.kt
index edf4bcc238c0..aac5e57207a7 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/education/data/repository/ContextualEducationRepositoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/education/data/repository/ContextualEducationRepositoryKosmos.kt
@@ -21,6 +21,6 @@ import java.time.Clock
import java.time.Instant
var Kosmos.contextualEducationRepository: ContextualEducationRepository by
- Kosmos.Fixture { FakeContextualEducationRepository(fakeEduClock) }
+ Kosmos.Fixture { FakeContextualEducationRepository() }
var Kosmos.fakeEduClock: Clock by Kosmos.Fixture { FakeEduClock(Instant.MIN) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/education/data/repository/FakeContextualEducationRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/education/data/repository/FakeContextualEducationRepository.kt
index 3816e1b604ce..aa1968afba7d 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/education/data/repository/FakeContextualEducationRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/education/data/repository/FakeContextualEducationRepository.kt
@@ -18,12 +18,11 @@ package com.android.systemui.education.data.repository
import com.android.systemui.contextualeducation.GestureType
import com.android.systemui.education.data.model.GestureEduModel
-import java.time.Clock
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
-class FakeContextualEducationRepository(private val clock: Clock) : ContextualEducationRepository {
+class FakeContextualEducationRepository : ContextualEducationRepository {
private val userGestureMap = mutableMapOf<Int, GestureEduModel>()
private val _gestureEduModels = MutableStateFlow(GestureEduModel())
@@ -44,16 +43,11 @@ class FakeContextualEducationRepository(private val clock: Clock) : ContextualEd
return gestureEduModelsFlow
}
- override suspend fun incrementSignalCount(gestureType: GestureType) {
- val originalModel = _gestureEduModels.value
- _gestureEduModels.value =
- originalModel.copy(
- signalCount = _gestureEduModels.value.signalCount + 1,
- )
- }
-
- override suspend fun updateShortcutTriggerTime(gestureType: GestureType) {
- val originalModel = _gestureEduModels.value
- _gestureEduModels.value = originalModel.copy(lastShortcutTriggeredTime = clock.instant())
+ override suspend fun updateGestureEduModel(
+ gestureType: GestureType,
+ transform: (GestureEduModel) -> GestureEduModel
+ ) {
+ val currentModel = _gestureEduModels.value
+ _gestureEduModels.value = transform(currentModel)
}
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/education/domain/interactor/ContextualEducationInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/education/domain/interactor/ContextualEducationInteractorKosmos.kt
index a7b322b5a86d..5c99a7faf13c 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/education/domain/interactor/ContextualEducationInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/education/domain/interactor/ContextualEducationInteractorKosmos.kt
@@ -17,6 +17,7 @@
package com.android.systemui.education.domain.interactor
import com.android.systemui.education.data.repository.contextualEducationRepository
+import com.android.systemui.education.data.repository.fakeEduClock
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testDispatcher
import com.android.systemui.kosmos.testScope
@@ -28,6 +29,7 @@ val Kosmos.contextualEducationInteractor by
backgroundScope = testScope.backgroundScope,
backgroundDispatcher = testDispatcher,
repository = contextualEducationRepository,
- selectedUserInteractor = selectedUserInteractor
+ selectedUserInteractor = selectedUserInteractor,
+ clock = fakeEduClock
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/NotificationsShadeWindowModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/NotificationsShadeWindowModelKosmos.kt
index cd4fab8d2970..6252d4498a5e 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/NotificationsShadeWindowModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/NotificationsShadeWindowModelKosmos.kt
@@ -16,8 +16,14 @@
package com.android.systemui.shade.ui.viewmodel
+import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
import com.android.systemui.kosmos.Kosmos
val Kosmos.notificationShadeWindowModel: NotificationShadeWindowModel by
- Kosmos.Fixture { NotificationShadeWindowModel(keyguardTransitionInteractor) }
+ Kosmos.Fixture {
+ NotificationShadeWindowModel(
+ keyguardTransitionInteractor,
+ keyguardInteractor,
+ )
+ }
diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
index 31232687418f..0b6d1358bd57 100644
--- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
+++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
@@ -51,6 +51,7 @@ import java.util.HashMap;
import java.util.Map;
import java.util.List;
import java.util.ArrayList;
+import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
/**
* Maps system settings to system properties.
@@ -345,7 +346,7 @@ public class SettingsToPropertiesMapper {
// add sys prop sync callback for staged flag values
DeviceConfig.addOnPropertiesChangedListener(
NAMESPACE_REBOOT_STAGING,
- AsyncTask.THREAD_POOL_EXECUTOR,
+ newSingleThreadScheduledExecutor(),
(DeviceConfig.Properties properties) -> {
for (String flagName : properties.getKeyset()) {
diff --git a/services/core/java/com/android/server/audio/TEST_MAPPING b/services/core/java/com/android/server/audio/TEST_MAPPING
index 2cea32af2396..f050090e69c1 100644
--- a/services/core/java/com/android/server/audio/TEST_MAPPING
+++ b/services/core/java/com/android/server/audio/TEST_MAPPING
@@ -10,6 +10,9 @@
"include-filter": "android.media.audio.cts.AudioFocusTest"
},
{
+ "include-filter": "android.media.audio.cts.AudioPlaybackCaptureTest"
+ },
+ {
"include-filter": "android.media.audio.cts.SpatializerTest"
}
]
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index b09b07bd1e83..98ea70dcff98 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -1331,6 +1331,17 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl.
}
updateFromSettingsLocked(true, newUserId);
+ // Special workaround for b/356879517.
+ // KeyboardLayoutManager still expects onInputMethodSubtypeChangedForKeyboardLayoutMapping
+ // to be called back upon IME user switching, while we are actively deprecating the concept
+ // of "current IME user" at b/350386877.
+ // TODO(b/356879517): Come up with a way to avoid this special handling.
+ if (newUserData.mSubtypeForKeyboardLayoutMapping != null) {
+ final var subtypeHandleAndSubtype = newUserData.mSubtypeForKeyboardLayoutMapping;
+ mInputManagerInternal.onInputMethodSubtypeChangedForKeyboardLayoutMapping(
+ newUserId, subtypeHandleAndSubtype.first, subtypeHandleAndSubtype.second);
+ }
+
if (initialUserSwitch) {
InputMethodUtils.setNonSelectedSystemImesDisabledUntilUsed(
getPackageManagerForUser(mContext, newUserId),
@@ -2913,6 +2924,17 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl.
? subtype : null;
final InputMethodSubtypeHandle newSubtypeHandle = normalizedSubtype != null
? InputMethodSubtypeHandle.of(imi, normalizedSubtype) : null;
+
+ final var userData = getUserData(userId);
+
+ // A workaround for b/356879517. KeyboardLayoutManager has relied on an implementation
+ // detail that IMMS triggers this callback only for the current IME user.
+ // TODO(b/357663774): Figure out how to better handle this scenario.
+ userData.mSubtypeForKeyboardLayoutMapping =
+ Pair.create(newSubtypeHandle, normalizedSubtype);
+ if (userId != mCurrentUserId) {
+ return;
+ }
mInputManagerInternal.onInputMethodSubtypeChangedForKeyboardLayoutMapping(
userId, newSubtypeHandle, normalizedSubtype);
}
@@ -6552,17 +6574,6 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl.
out.print(imeId);
out.print(" selected for user #");
out.println(userId);
-
- // Workaround for b/354782333.
- final var settingsValue = SecureSettingsWrapper.getString(
- Settings.Secure.DEFAULT_INPUT_METHOD, "", userId);
- if (!TextUtils.equals(settingsValue, imeId)) {
- Slog.w(TAG, "DEFAULT_INPUT_METHOD=" + settingsValue
- + " is not updated. Fixing it up to " + imeId
- + " See b/354782333.");
- SecureSettingsWrapper.putString(
- Settings.Secure.DEFAULT_INPUT_METHOD, imeId, userId);
- }
}
hasFailed |= failedToSelectUnknownIme;
}
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodSubtypeSwitchingController.java b/services/core/java/com/android/server/inputmethod/InputMethodSubtypeSwitchingController.java
index 05cc5985a8cc..b5c278ca1c52 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodSubtypeSwitchingController.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodSubtypeSwitchingController.java
@@ -591,13 +591,13 @@ final class InputMethodSubtypeSwitchingController {
pw.println(prefix + "Static order:");
for (int i = 0; i < mItems.size(); ++i) {
final var item = mItems.get(i);
- pw.println(prefix + "i=" + i + " item=" + item);
+ pw.println(prefix + " i=" + i + " item=" + item);
}
pw.println(prefix + "Recency order:");
for (int i = 0; i < mRecencyMap.length; ++i) {
final int index = mRecencyMap[i];
final var item = mItems.get(index);
- pw.println(prefix + "i=" + i + " item=" + item);
+ pw.println(prefix + " i=" + i + " item=" + item);
}
}
}
@@ -800,7 +800,7 @@ final class InputMethodSubtypeSwitchingController {
pw.println(prefix + "mHardwareRotationList:");
mHardwareRotationList.dump(pw, prefix + " ");
}
- pw.println("User action since last switch: " + mUserActionSinceSwitch);
+ pw.println(prefix + "User action since last switch: " + mUserActionSinceSwitch);
}
}
}
diff --git a/services/core/java/com/android/server/inputmethod/UserData.java b/services/core/java/com/android/server/inputmethod/UserData.java
index 28394c6a6272..c0d3428b50c3 100644
--- a/services/core/java/com/android/server/inputmethod/UserData.java
+++ b/services/core/java/com/android/server/inputmethod/UserData.java
@@ -19,14 +19,17 @@ package com.android.server.inputmethod;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
+import android.util.Pair;
import android.util.SparseArray;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.ImeTracker;
+import android.view.inputmethod.InputMethodSubtype;
import android.window.ImeOnBackInvokedDispatcher;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.inputmethod.IRemoteAccessibilityInputConnection;
import com.android.internal.inputmethod.IRemoteInputConnection;
+import com.android.internal.inputmethod.InputMethodSubtypeHandle;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -149,6 +152,16 @@ final class UserData {
String mLastEnabledInputMethodsStr = "";
/**
+ * A temporary solution to Bug 356879517, where we need to emulate the previous single-user mode
+ * behavior for KeyboardLayoutManager.
+ *
+ * <p>TODO(b/357663774): Remove this workaround</p>
+ */
+ @GuardedBy("ImfLock.class")
+ @Nullable
+ Pair<InputMethodSubtypeHandle, InputMethodSubtype> mSubtypeForKeyboardLayoutMapping;
+
+ /**
* {@code true} when the IME is responsible for drawing the navigation bar and its buttons.
*/
@NonNull
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 53b67969e91a..17f6561cb757 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -622,16 +622,6 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
@GuardedBy("mUidRulesFirstLock")
final SparseIntArray mUidFirewallStandbyRules = new SparseIntArray();
- @GuardedBy("mUidRulesFirstLock")
- final SparseIntArray mUidFirewallDozableRules = new SparseIntArray();
- @GuardedBy("mUidRulesFirstLock")
- final SparseIntArray mUidFirewallPowerSaveRules = new SparseIntArray();
- @GuardedBy("mUidRulesFirstLock")
- final SparseIntArray mUidFirewallBackgroundRules = new SparseIntArray();
- @GuardedBy("mUidRulesFirstLock")
- final SparseIntArray mUidFirewallRestrictedModeRules = new SparseIntArray();
- @GuardedBy("mUidRulesFirstLock")
- final SparseIntArray mUidFirewallLowPowerStandbyModeRules = new SparseIntArray();
/** Set of states for the child firewall chains. True if the chain is active. */
@GuardedBy("mUidRulesFirstLock")
@@ -4589,7 +4579,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
@VisibleForTesting
@GuardedBy("mUidRulesFirstLock")
void updateRestrictedModeAllowlistUL() {
- mUidFirewallRestrictedModeRules.clear();
+ final SparseIntArray uidRules = new SparseIntArray();
forEachUid("updateRestrictedModeAllowlist", uid -> {
synchronized (mUidRulesFirstLock) {
final int effectiveBlockedReasons = updateBlockedReasonsForRestrictedModeUL(
@@ -4599,13 +4589,13 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
// setUidFirewallRulesUL will allowlist all uids that are passed to it, so only add
// non-default rules.
if (newFirewallRule != FIREWALL_RULE_DEFAULT) {
- mUidFirewallRestrictedModeRules.append(uid, newFirewallRule);
+ uidRules.append(uid, newFirewallRule);
}
}
});
if (mRestrictedNetworkingMode) {
// firewall rules only need to be set when this mode is being enabled.
- setUidFirewallRulesUL(FIREWALL_CHAIN_RESTRICTED, mUidFirewallRestrictedModeRules);
+ setUidFirewallRulesUL(FIREWALL_CHAIN_RESTRICTED, uidRules);
}
enableFirewallChainUL(FIREWALL_CHAIN_RESTRICTED, mRestrictedNetworkingMode);
}
@@ -4689,8 +4679,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
void updateRulesForPowerSaveUL() {
Trace.traceBegin(Trace.TRACE_TAG_NETWORK, "updateRulesForPowerSaveUL");
try {
- updateRulesForAllowlistedPowerSaveUL(mRestrictPower, FIREWALL_CHAIN_POWERSAVE,
- mUidFirewallPowerSaveRules);
+ updateRulesForAllowlistedPowerSaveUL(mRestrictPower, FIREWALL_CHAIN_POWERSAVE);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_NETWORK);
}
@@ -4705,8 +4694,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
void updateRulesForDeviceIdleUL() {
Trace.traceBegin(Trace.TRACE_TAG_NETWORK, "updateRulesForDeviceIdleUL");
try {
- updateRulesForAllowlistedPowerSaveUL(mDeviceIdleMode, FIREWALL_CHAIN_DOZABLE,
- mUidFirewallDozableRules);
+ updateRulesForAllowlistedPowerSaveUL(mDeviceIdleMode, FIREWALL_CHAIN_DOZABLE);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_NETWORK);
}
@@ -4720,13 +4708,11 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
// NOTE: since both fw_dozable and fw_powersave uses the same map
// (mPowerSaveTempWhitelistAppIds) for allowlisting, we can reuse their logic in this method.
@GuardedBy("mUidRulesFirstLock")
- private void updateRulesForAllowlistedPowerSaveUL(boolean enabled, int chain,
- SparseIntArray rules) {
+ private void updateRulesForAllowlistedPowerSaveUL(boolean enabled, int chain) {
if (enabled) {
// Sync the allowlists before enabling the chain. We don't care about the rules if
// we are disabling the chain.
- final SparseIntArray uidRules = rules;
- uidRules.clear();
+ final SparseIntArray uidRules = new SparseIntArray();
final List<UserInfo> users = mUserManager.getUsers();
for (int ui = users.size() - 1; ui >= 0; ui--) {
UserInfo user = users.get(ui);
@@ -4755,9 +4741,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
private void updateRulesForBackgroundChainUL() {
Trace.traceBegin(Trace.TRACE_TAG_NETWORK, "updateRulesForBackgroundChainUL");
try {
- final SparseIntArray uidRules = mUidFirewallBackgroundRules;
- uidRules.clear();
-
+ final SparseIntArray uidRules = new SparseIntArray();
final List<UserInfo> users = mUserManager.getUsers();
for (int ui = users.size() - 1; ui >= 0; ui--) {
final UserInfo user = users.get(ui);
@@ -4794,17 +4778,17 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
Trace.traceBegin(Trace.TRACE_TAG_NETWORK, "updateRulesForLowPowerStandbyUL");
try {
if (mLowPowerStandbyActive) {
- mUidFirewallLowPowerStandbyModeRules.clear();
+ final SparseIntArray uidRules = new SparseIntArray();
for (int i = mUidState.size() - 1; i >= 0; i--) {
final int uid = mUidState.keyAt(i);
final int effectiveBlockedReasons = getEffectiveBlockedReasons(uid);
if (hasInternetPermissionUL(uid) && (effectiveBlockedReasons
& BLOCKED_REASON_LOW_POWER_STANDBY) == 0) {
- mUidFirewallLowPowerStandbyModeRules.put(uid, FIREWALL_RULE_ALLOW);
+ uidRules.put(uid, FIREWALL_RULE_ALLOW);
}
}
setUidFirewallRulesUL(FIREWALL_CHAIN_LOW_POWER_STANDBY,
- mUidFirewallLowPowerStandbyModeRules, CHAIN_TOGGLE_ENABLE);
+ uidRules, CHAIN_TOGGLE_ENABLE);
} else {
setUidFirewallRulesUL(FIREWALL_CHAIN_LOW_POWER_STANDBY, null, CHAIN_TOGGLE_DISABLE);
}
@@ -4822,10 +4806,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
final int effectiveBlockedReasons = getEffectiveBlockedReasons(uid);
if (mUidState.contains(uid)
&& (effectiveBlockedReasons & BLOCKED_REASON_LOW_POWER_STANDBY) == 0) {
- mUidFirewallLowPowerStandbyModeRules.put(uid, FIREWALL_RULE_ALLOW);
setUidFirewallRuleUL(FIREWALL_CHAIN_LOW_POWER_STANDBY, uid, FIREWALL_RULE_ALLOW);
} else {
- mUidFirewallLowPowerStandbyModeRules.delete(uid);
setUidFirewallRuleUL(FIREWALL_CHAIN_LOW_POWER_STANDBY, uid, FIREWALL_RULE_DEFAULT);
}
}
@@ -5313,16 +5295,11 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
mActivityManagerInternal.onUidBlockedReasonsChanged(uid, BLOCKED_REASON_NONE);
mUidPolicy.delete(uid);
mUidFirewallStandbyRules.delete(uid);
- mUidFirewallDozableRules.delete(uid);
- mUidFirewallPowerSaveRules.delete(uid);
- mUidFirewallBackgroundRules.delete(uid);
mBackgroundTransitioningUids.delete(uid);
mPowerSaveWhitelistExceptIdleAppIds.delete(uid);
mPowerSaveWhitelistAppIds.delete(uid);
mPowerSaveTempWhitelistAppIds.delete(uid);
mAppIdleTempWhitelistAppIds.delete(uid);
- mUidFirewallRestrictedModeRules.delete(uid);
- mUidFirewallLowPowerStandbyModeRules.delete(uid);
synchronized (mUidStateCallbackInfos) {
mUidStateCallbackInfos.remove(uid);
}
@@ -6269,18 +6246,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
"setUidFirewallRuleUL: " + chain + "/" + uid + "/" + rule);
}
try {
- if (chain == FIREWALL_CHAIN_DOZABLE) {
- mUidFirewallDozableRules.put(uid, rule);
- } else if (chain == FIREWALL_CHAIN_STANDBY) {
+ if (chain == FIREWALL_CHAIN_STANDBY) {
mUidFirewallStandbyRules.put(uid, rule);
- } else if (chain == FIREWALL_CHAIN_POWERSAVE) {
- mUidFirewallPowerSaveRules.put(uid, rule);
- } else if (chain == FIREWALL_CHAIN_RESTRICTED) {
- mUidFirewallRestrictedModeRules.put(uid, rule);
- } else if (chain == FIREWALL_CHAIN_LOW_POWER_STANDBY) {
- mUidFirewallLowPowerStandbyModeRules.put(uid, rule);
- } else if (chain == FIREWALL_CHAIN_BACKGROUND) {
- mUidFirewallBackgroundRules.put(uid, rule);
}
// Note that we do not need keep a separate cache of uid rules for chains that we do
// not call #setUidFirewallRulesUL for.
diff --git a/services/core/java/com/android/server/vibrator/HapticFeedbackCustomization.java b/services/core/java/com/android/server/vibrator/HapticFeedbackCustomization.java
index 503a7268d5d3..65fc7b2c5c39 100644
--- a/services/core/java/com/android/server/vibrator/HapticFeedbackCustomization.java
+++ b/services/core/java/com/android/server/vibrator/HapticFeedbackCustomization.java
@@ -18,6 +18,7 @@ package com.android.server.vibrator;
import android.annotation.Nullable;
import android.content.res.Resources;
+import android.content.res.XmlResourceParser;
import android.os.VibrationEffect;
import android.os.VibratorInfo;
import android.os.vibrator.Flags;
@@ -28,6 +29,7 @@ import android.util.Slog;
import android.util.SparseArray;
import android.util.Xml;
+import com.android.internal.util.XmlUtils;
import com.android.internal.vibrator.persistence.XmlParserException;
import com.android.internal.vibrator.persistence.XmlReader;
import com.android.internal.vibrator.persistence.XmlValidator;
@@ -39,6 +41,7 @@ import org.xmlpull.v1.XmlPullParserException;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
+import java.io.Reader;
/**
* Class that loads custom {@link VibrationEffect} to be performed for each
@@ -127,27 +130,19 @@ final class HapticFeedbackCustomization {
Slog.d(TAG, "Haptic feedback customization feature is not enabled.");
return null;
}
- String customizationFile =
- res.getString(
- com.android.internal.R.string.config_hapticFeedbackCustomizationFile);
- if (TextUtils.isEmpty(customizationFile)) {
- Slog.d(TAG, "Customization file not configured.");
- return null;
- }
- FileReader fileReader;
- try {
- fileReader = new FileReader(customizationFile);
- } catch (FileNotFoundException e) {
- Slog.d(TAG, "Specified customization file not found.");
- return null;
+ // Old loading path that reads customization from file at dir defined by config.
+ TypedXmlPullParser parser = readCustomizationFile(res);
+ if (parser == null) {
+ // When old loading path doesn't succeed, try loading customization from resources.
+ parser = readCustomizationResources(res);
+ }
+ if (parser == null) {
+ Slog.d(TAG, "No loadable haptic feedback customization.");
+ return null;
}
- TypedXmlPullParser parser = Xml.newFastPullParser();
- parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
- parser.setInput(fileReader);
-
- XmlReader.readDocumentStartTag(parser, TAG_CONSTANTS);
+ XmlUtils.beginDocument(parser, TAG_CONSTANTS);
XmlValidator.checkTagHasNoUnexpectedAttributes(parser);
int rootDepth = parser.getDepth();
@@ -191,6 +186,46 @@ final class HapticFeedbackCustomization {
return mapping;
}
+ // TODO(b/356412421): deprecate old path related files.
+ private static TypedXmlPullParser readCustomizationFile(Resources res)
+ throws XmlPullParserException {
+ String customizationFile = res.getString(
+ com.android.internal.R.string.config_hapticFeedbackCustomizationFile);
+ if (TextUtils.isEmpty(customizationFile)) {
+ return null;
+ }
+
+ final Reader customizationReader;
+ try {
+ customizationReader = new FileReader(customizationFile);
+ } catch (FileNotFoundException e) {
+ Slog.e(TAG, "Specified customization file not found.", e);
+ return null;
+ }
+
+ final TypedXmlPullParser parser;
+ parser = Xml.newFastPullParser();
+ parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
+ parser.setInput(customizationReader);
+ Slog.d(TAG, "Successfully opened customization file.");
+ return parser;
+ }
+
+ private static TypedXmlPullParser readCustomizationResources(Resources res) {
+ if (!Flags.loadHapticFeedbackVibrationCustomizationFromResources()) {
+ return null;
+ }
+ final XmlResourceParser resParser;
+ try {
+ resParser = res.getXml(com.android.internal.R.xml.haptic_feedback_customization);
+ } catch (Resources.NotFoundException e) {
+ Slog.e(TAG, "Haptic customization resource not found.", e);
+ return null;
+ }
+ Slog.d(TAG, "Successfully opened customization resource.");
+ return XmlUtils.makeTyped(resParser);
+ }
+
/**
* Represents an error while parsing a haptic feedback customization XML.
*/
diff --git a/services/core/java/com/android/server/vibrator/HapticFeedbackVibrationProvider.java b/services/core/java/com/android/server/vibrator/HapticFeedbackVibrationProvider.java
index 3f9da82e3d2e..eccbffb53529 100644
--- a/services/core/java/com/android/server/vibrator/HapticFeedbackVibrationProvider.java
+++ b/services/core/java/com/android/server/vibrator/HapticFeedbackVibrationProvider.java
@@ -22,7 +22,6 @@ import android.os.VibrationAttributes;
import android.os.VibrationEffect;
import android.os.Vibrator;
import android.os.VibratorInfo;
-import android.os.vibrator.Flags;
import android.util.Slog;
import android.util.SparseArray;
import android.view.HapticFeedbackConstants;
@@ -348,7 +347,7 @@ public final class HapticFeedbackVibrationProvider {
predefinedEffectId = VibrationEffect.EFFECT_CLICK;
predefinedEffectFallback = true;
}
- if (Flags.keyboardCategoryEnabled() && mKeyboardVibrationFixedAmplitude > 0) {
+ if (mKeyboardVibrationFixedAmplitude > 0) {
if (mVibratorInfo.isPrimitiveSupported(primitiveId)) {
return VibrationEffect.startComposition()
.addPrimitive(primitiveId, mKeyboardVibrationFixedAmplitude)
@@ -361,10 +360,6 @@ public final class HapticFeedbackVibrationProvider {
private VibrationAttributes createKeyboardVibrationAttributes(
@HapticFeedbackConstants.PrivateFlags int privFlags) {
- // Use touch attribute when the keyboard category is disable.
- if (!Flags.keyboardCategoryEnabled()) {
- return TOUCH_VIBRATION_ATTRIBUTES;
- }
// Use touch attribute when the haptic is not apply to IME.
if ((privFlags & HapticFeedbackConstants.PRIVATE_FLAG_APPLY_INPUT_METHOD_SETTINGS) == 0) {
return TOUCH_VIBRATION_ATTRIBUTES;
diff --git a/services/core/java/com/android/server/vibrator/VibrationSettings.java b/services/core/java/com/android/server/vibrator/VibrationSettings.java
index fb92d609f1cf..f2f5eda7c05a 100644
--- a/services/core/java/com/android/server/vibrator/VibrationSettings.java
+++ b/services/core/java/com/android/server/vibrator/VibrationSettings.java
@@ -55,7 +55,6 @@ import android.os.VibrationAttributes;
import android.os.VibrationEffect;
import android.os.Vibrator;
import android.os.Vibrator.VibrationIntensity;
-import android.os.vibrator.Flags;
import android.os.vibrator.VibrationConfig;
import android.provider.Settings;
import android.util.IndentingPrintWriter;
@@ -533,8 +532,7 @@ final class VibrationSettings {
return false;
}
- if (Flags.keyboardCategoryEnabled()
- && mVibrationConfig.isKeyboardVibrationSettingsSupported()) {
+ if (mVibrationConfig.isKeyboardVibrationSettingsSupported()) {
int category = callerInfo.attrs.getCategory();
if (usage == USAGE_TOUCH && category == CATEGORY_KEYBOARD) {
// Keyboard touch has a different user setting.
diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java
index 8ae4f9a41efb..6afcae797277 100644
--- a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java
+++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java
@@ -45,7 +45,11 @@ import android.annotation.Nullable;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
+import android.platform.test.annotations.RequiresFlagsDisabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.view.Display;
+import android.view.inputmethod.Flags;
import android.view.inputmethod.ImeTracker;
import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -58,6 +62,7 @@ import com.android.internal.inputmethod.StartInputFlags;
import com.android.internal.inputmethod.StartInputReason;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -70,6 +75,9 @@ import org.junit.runner.RunWith;
*/
@RunWith(AndroidJUnit4.class)
public class DefaultImeVisibilityApplierTest extends InputMethodManagerServiceTestBase {
+
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
private DefaultImeVisibilityApplier mVisibilityApplier;
@Before
@@ -112,6 +120,7 @@ public class DefaultImeVisibilityApplierTest extends InputMethodManagerServiceTe
}
@Test
+ @RequiresFlagsDisabled(Flags.FLAG_REFACTOR_INSETS_CONTROLLER)
public void testApplyImeVisibility_showIme() {
final var statsToken = ImeTracker.Token.empty();
synchronized (ImfLock.class) {
@@ -122,6 +131,7 @@ public class DefaultImeVisibilityApplierTest extends InputMethodManagerServiceTe
}
@Test
+ @RequiresFlagsDisabled(Flags.FLAG_REFACTOR_INSETS_CONTROLLER)
public void testApplyImeVisibility_hideIme() {
final var statsToken = ImeTracker.Token.empty();
synchronized (ImfLock.class) {
@@ -141,7 +151,12 @@ public class DefaultImeVisibilityApplierTest extends InputMethodManagerServiceTe
mVisibilityApplier.applyImeVisibility(mWindowToken, ImeTracker.Token.empty(),
STATE_HIDE_IME_EXPLICIT, eq(SoftInputShowHideReason.NOT_SET), mUserId);
}
- verifyHideSoftInput(true, true);
+ if (Flags.refactorInsetsController()) {
+ verifySetImeVisibility(true /* setVisible */, false /* invoked */);
+ verifySetImeVisibility(false /* setVisible */, true /* invoked */);
+ } else {
+ verifyHideSoftInput(true, true);
+ }
}
@Test
@@ -153,7 +168,12 @@ public class DefaultImeVisibilityApplierTest extends InputMethodManagerServiceTe
mVisibilityApplier.applyImeVisibility(mWindowToken, ImeTracker.Token.empty(),
STATE_HIDE_IME_NOT_ALWAYS, eq(SoftInputShowHideReason.NOT_SET), mUserId);
}
- verifyHideSoftInput(true, true);
+ if (Flags.refactorInsetsController()) {
+ verifySetImeVisibility(true /* setVisible */, false /* invoked */);
+ verifySetImeVisibility(false /* setVisible */, true /* invoked */);
+ } else {
+ verifyHideSoftInput(true, true);
+ }
}
@Test
@@ -162,10 +182,16 @@ public class DefaultImeVisibilityApplierTest extends InputMethodManagerServiceTe
mVisibilityApplier.applyImeVisibility(mWindowToken, ImeTracker.Token.empty(),
STATE_SHOW_IME_IMPLICIT, eq(SoftInputShowHideReason.NOT_SET), mUserId);
}
- verifyShowSoftInput(true, true, 0 /* showFlags */);
+ if (Flags.refactorInsetsController()) {
+ verifySetImeVisibility(true /* setVisible */, true /* invoked */);
+ verifySetImeVisibility(false /* setVisible */, false /* invoked */);
+ } else {
+ verifyShowSoftInput(true, true, 0 /* showFlags */);
+ }
}
@Test
+ @RequiresFlagsDisabled(Flags.FLAG_REFACTOR_INSETS_CONTROLLER)
public void testApplyImeVisibility_hideImeFromTargetOnSecondaryDisplay() {
// Init a IME target client on the secondary display to show IME.
mInputMethodManagerService.addClient(mMockInputMethodClient, mMockRemoteInputConnection,
@@ -234,8 +260,10 @@ public class DefaultImeVisibilityApplierTest extends InputMethodManagerServiceTe
verify(mVisibilityApplier).applyImeVisibility(
eq(mWindowToken), any(), eq(STATE_HIDE_IME),
eq(SoftInputShowHideReason.NOT_SET), eq(mUserId) /* userId */);
- verify(mInputMethodManagerService.mWindowManagerInternal).hideIme(
- eq(mWindowToken), eq(displayIdToShowIme), and(not(eq(statsToken)), notNull()));
+ if (!Flags.refactorInsetsController()) {
+ verify(mInputMethodManagerService.mWindowManagerInternal).hideIme(eq(mWindowToken),
+ eq(displayIdToShowIme), and(not(eq(statsToken)), notNull()));
+ }
}
}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
index 957ee06b6e27..1a3af138894d 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
@@ -1320,6 +1320,16 @@ public class FullScreenMagnificationGestureHandlerTest {
}
@Test
+ public void testSetScaleTo2() {
+ testSetScaleAndZoom(2.0f);
+ }
+
+ @Test
+ public void testSetScaleTo20() {
+ testSetScaleAndZoom(20.0f);
+ }
+
+ @Test
public void testTransitToPanningState_scaleDifferenceOverThreshold_startDetecting() {
final float scale = 2.0f;
final float threshold = FullScreenMagnificationGestureHandler.PanningScalingState
@@ -1698,6 +1708,18 @@ public class FullScreenMagnificationGestureHandlerTest {
assertActionsInOrder(eventCaptor.mEvents, expectedActions);
}
+ private void testSetScaleAndZoom(float scale) {
+ Settings.Secure.putFloatForUser(mContext.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE, scale,
+ UserHandle.USER_SYSTEM);
+
+ goFromStateIdleTo(STATE_ACTIVATED_2TAPS);
+
+ check(mMgh.mCurrentState == mMgh.mDetectingState, STATE_IDLE);
+ assertThat(mMgh.mFullScreenMagnificationController.getScale(DISPLAY_0))
+ .isEqualTo(scale);
+ }
+
private void enableOneFingerPanning(boolean enable) {
mMockOneFingerPanningEnabled = enable;
when(mMockOneFingerPanningSettingsProvider.isOneFingerPanningEnabled()).thenReturn(enable);
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationConnectionManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationConnectionManagerTest.java
index 87fe6cf8f283..1c0ddc242ca6 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationConnectionManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationConnectionManagerTest.java
@@ -300,7 +300,7 @@ public class MagnificationConnectionManagerTest {
mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 2.5f, NaN, NaN);
- mMagnificationConnectionManager.setScale(TEST_DISPLAY, 10.0f);
+ mMagnificationConnectionManager.setScale(TEST_DISPLAY, 22.0f);
assertEquals(mMagnificationConnectionManager.getScale(TEST_DISPLAY),
MagnificationScaleProvider.MAX_SCALE);
diff --git a/services/tests/vibrator/Android.bp b/services/tests/vibrator/Android.bp
index 757bcd8e2193..43ad44f057cc 100644
--- a/services/tests/vibrator/Android.bp
+++ b/services/tests/vibrator/Android.bp
@@ -32,6 +32,7 @@ android_test {
"frameworks-base-testutils",
"frameworks-services-vibrator-testutils",
"junit",
+ "junit-params",
"mockito-target-inline-minus-junit4",
"platform-test-annotations",
"service-permission.stubs.system_server",
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/HapticFeedbackCustomizationTest.java b/services/tests/vibrator/src/com/android/server/vibrator/HapticFeedbackCustomizationTest.java
index 2b23b1897f59..e0d05df1de80 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/HapticFeedbackCustomizationTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/HapticFeedbackCustomizationTest.java
@@ -16,16 +16,17 @@
package com.android.server.vibrator;
-
import static android.os.VibrationEffect.Composition.PRIMITIVE_TICK;
import static android.os.VibrationEffect.EFFECT_CLICK;
+import static com.android.internal.R.xml.haptic_feedback_customization;
import static com.android.server.vibrator.HapticFeedbackCustomization.CustomizationParserException;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertThrows;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.when;
import android.content.res.Resources;
@@ -39,10 +40,15 @@ import android.util.SparseArray;
import androidx.test.InstrumentationRegistry;
import com.android.internal.R;
+import com.android.internal.annotations.Keep;
+
+import junitparams.JUnitParamsRunner;
+import junitparams.Parameters;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
+import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
@@ -50,6 +56,7 @@ import org.mockito.junit.MockitoRule;
import java.io.File;
import java.io.FileOutputStream;
+@RunWith(JUnitParamsRunner.class)
public class HapticFeedbackCustomizationTest {
@Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
@@ -78,21 +85,35 @@ public class HapticFeedbackCustomizationTest {
@Mock private Resources mResourcesMock;
@Mock private VibratorInfo mVibratorInfoMock;
+ @Keep
+ private static Object[][] hapticFeedbackCustomizationTestArguments() {
+ // (boolean hasConfigFile, boolean hasRes).
+ return new Object[][] {{true, true}, {true, false}, {false, true}};
+ }
+
@Before
public void setUp() {
when(mVibratorInfoMock.areVibrationFeaturesSupported(any())).thenReturn(true);
mSetFlagsRule.enableFlags(Flags.FLAG_HAPTIC_FEEDBACK_VIBRATION_OEM_CUSTOMIZATION_ENABLED);
+ mSetFlagsRule.disableFlags(
+ Flags.FLAG_LOAD_HAPTIC_FEEDBACK_VIBRATION_CUSTOMIZATION_FROM_RESOURCES);
}
@Test
- public void testParseCustomizations_noCustomization_success() throws Exception {
- assertParseCustomizationsSucceeds(
- /* xml= */ "<haptic-feedback-constants></haptic-feedback-constants>",
- /* expectedCustomizations= */ new SparseArray<>());
+ @Parameters(method = "hapticFeedbackCustomizationTestArguments")
+ public void testParseCustomizations_noCustomization_success(
+ boolean hasConfigFile, boolean hasRes) throws Exception {
+ String xml = "<haptic-feedback-constants></haptic-feedback-constants>";
+ SparseArray<VibrationEffect> expectedMapping = new SparseArray<>();
+ setupParseCustomizations(xml, hasConfigFile, hasRes);
+
+ assertParseCustomizationsSucceeds(xml, expectedMapping, hasConfigFile, hasRes);
}
@Test
- public void testParseCustomizations_featureFlagDisabled_returnsNull() throws Exception {
+ @Parameters(method = "hapticFeedbackCustomizationTestArguments")
+ public void testParseCustomizations_featureFlagDisabled_returnsNull(
+ boolean hasConfigFile, boolean hasRes) throws Exception {
mSetFlagsRule.disableFlags(Flags.FLAG_HAPTIC_FEEDBACK_VIBRATION_OEM_CUSTOMIZATION_ENABLED);
// Valid customization XML.
String xml = "<haptic-feedback-constants>"
@@ -100,14 +121,16 @@ public class HapticFeedbackCustomizationTest {
+ COMPOSITION_VIBRATION_XML
+ "</constant>"
+ "</haptic-feedback-constants>";
- setupCustomizationFile(xml);
+ setupParseCustomizations(xml, hasConfigFile, hasRes);
assertThat(HapticFeedbackCustomization.loadVibrations(mResourcesMock, mVibratorInfoMock))
.isNull();
}
@Test
- public void testParseCustomizations_oneVibrationCustomization_success() throws Exception {
+ @Parameters(method = "hapticFeedbackCustomizationTestArguments")
+ public void testParseCustomizations_oneVibrationCustomization_success(
+ boolean hasConfigFile, boolean hasRes) throws Exception {
String xml = "<haptic-feedback-constants>"
+ "<constant id=\"10\">"
+ COMPOSITION_VIBRATION_XML
@@ -116,11 +139,13 @@ public class HapticFeedbackCustomizationTest {
SparseArray<VibrationEffect> expectedMapping = new SparseArray<>();
expectedMapping.put(10, COMPOSITION_VIBRATION);
- assertParseCustomizationsSucceeds(xml, expectedMapping);
+ assertParseCustomizationsSucceeds(xml, expectedMapping, hasConfigFile, hasRes);
}
@Test
- public void testParseCustomizations_oneVibrationSelectCustomization_success() throws Exception {
+ @Parameters(method = "hapticFeedbackCustomizationTestArguments")
+ public void testParseCustomizations_oneVibrationSelectCustomization_success(
+ boolean hasConfigFile, boolean hasRes) throws Exception {
String xml = "<haptic-feedback-constants>"
+ "<constant id=\"10\">"
+ "<vibration-select>"
@@ -131,11 +156,13 @@ public class HapticFeedbackCustomizationTest {
SparseArray<VibrationEffect> expectedMapping = new SparseArray<>();
expectedMapping.put(10, COMPOSITION_VIBRATION);
- assertParseCustomizationsSucceeds(xml, expectedMapping);
+ assertParseCustomizationsSucceeds(xml, expectedMapping, hasConfigFile, hasRes);
}
@Test
- public void testParseCustomizations_multipleCustomizations_success() throws Exception {
+ @Parameters(method = "hapticFeedbackCustomizationTestArguments")
+ public void testParseCustomizations_multipleCustomizations_success(
+ boolean hasConfigFile, boolean hasRes) throws Exception {
String xml = "<haptic-feedback-constants>"
+ "<constant id=\"1\">"
+ COMPOSITION_VIBRATION_XML
@@ -162,11 +189,13 @@ public class HapticFeedbackCustomizationTest {
expectedMapping.put(150, PREDEFINED_VIBRATION);
expectedMapping.put(10, WAVEFORM_VIBARTION);
- assertParseCustomizationsSucceeds(xml, expectedMapping);
+ assertParseCustomizationsSucceeds(xml, expectedMapping, hasConfigFile, hasRes);
}
@Test
- public void testParseCustomizations_multipleCustomizations_noSupportedVibration_success()
+ @Parameters(method = "hapticFeedbackCustomizationTestArguments")
+ public void testParseCustomizations_multipleCustomizations_noSupportedVibration_success(
+ boolean hasConfigFile, boolean hasRes)
throws Exception {
makeUnsupported(COMPOSITION_VIBRATION, PREDEFINED_VIBRATION, WAVEFORM_VIBARTION);
String xml = "<haptic-feedback-constants>"
@@ -189,13 +218,16 @@ public class HapticFeedbackCustomizationTest {
+ "</vibration-select>"
+ "</constant>"
+ "</haptic-feedback-constants>";
+ SparseArray<VibrationEffect> expectedMapping = new SparseArray<>();
- assertParseCustomizationsSucceeds(xml, new SparseArray<>());
+ assertParseCustomizationsSucceeds(xml, expectedMapping, hasConfigFile, hasRes);
}
@Test
- public void testParseCustomizations_multipleCustomizations_someUnsupportedVibration_success()
- throws Exception {
+ @Parameters(method = "hapticFeedbackCustomizationTestArguments")
+ public void testParseCustomizations_multipleCustomizations_someUnsupportedVibration_success(
+ boolean hasConfigFile, boolean hasRes)
+ throws Exception {
makeSupported(PREDEFINED_VIBRATION, WAVEFORM_VIBARTION);
makeUnsupported(COMPOSITION_VIBRATION);
String xml = "<haptic-feedback-constants>"
@@ -230,7 +262,7 @@ public class HapticFeedbackCustomizationTest {
expectedMapping.put(150, PREDEFINED_VIBRATION);
expectedMapping.put(10, PREDEFINED_VIBRATION);
- assertParseCustomizationsSucceeds(xml, expectedMapping);
+ assertParseCustomizationsSucceeds(xml, expectedMapping, hasConfigFile, hasRes);
}
@Test
@@ -252,12 +284,23 @@ public class HapticFeedbackCustomizationTest {
}
@Test
- public void testParseCustomizations_disallowedVibrationForHapticFeedback_throwsException()
- throws Exception {
+ public void testParseCustomizations_noCustomizationResource_returnsNull() throws Exception {
+ mSetFlagsRule.enableFlags(
+ Flags.FLAG_LOAD_HAPTIC_FEEDBACK_VIBRATION_CUSTOMIZATION_FROM_RESOURCES);
+ doThrow(new Resources.NotFoundException())
+ .when(mResourcesMock).getXml(haptic_feedback_customization);
+
+ assertThat(HapticFeedbackCustomization.loadVibrations(mResourcesMock, mVibratorInfoMock))
+ .isNull();
+ }
+
+ @Test
+ @Parameters(method = "hapticFeedbackCustomizationTestArguments")
+ public void testParseCustomizations_disallowedVibrationForHapticFeedback_throwsException(
+ boolean hasConfigFile, boolean hasRes) throws Exception {
// The XML content is good, but the serialized vibration is not supported for haptic
// feedback usage (i.e. repeating vibration).
- assertParseCustomizationsFails(
- "<haptic-feedback-constants>"
+ String xml = "<haptic-feedback-constants>"
+ "<constant id=\"10\">"
+ "<vibration-effect>"
+ "<waveform-effect>"
@@ -267,127 +310,139 @@ public class HapticFeedbackCustomizationTest {
+ "</waveform-effect>"
+ "</vibration-effect>"
+ "</constant>"
- + "</haptic-feedback-constants>");
+ + "</haptic-feedback-constants>";
+
+ assertParseCustomizationsFails(xml, hasConfigFile, hasRes);
}
@Test
- public void testParseCustomizations_emptyXml_throwsException() throws Exception {
- assertParseCustomizationsFails("");
+ @Parameters(method = "hapticFeedbackCustomizationTestArguments")
+ public void testParseCustomizations_emptyXml_throwsException(
+ boolean hasConfigFile, boolean hasRes) throws Exception {
+ assertParseCustomizationsFails("", hasConfigFile, hasRes);
}
@Test
- public void testParseCustomizations_noVibrationXml_throwsException() throws Exception {
- assertParseCustomizationsFails(
- "<haptic-feedback-constants>"
+ @Parameters(method = "hapticFeedbackCustomizationTestArguments")
+ public void testParseCustomizations_noVibrationXml_throwsException(
+ boolean hasConfigFile, boolean hasRes) throws Exception {
+ String xml = "<haptic-feedback-constants>"
+ "<constant id=\"1\">"
+ "</constant>"
- + "</haptic-feedback-constants>");
+ + "</haptic-feedback-constants>";
+
+ assertParseCustomizationsFails(xml, hasConfigFile, hasRes);
}
@Test
- public void testParseCustomizations_badEffectId_throwsException() throws Exception {
+ @Parameters(method = "hapticFeedbackCustomizationTestArguments")
+ public void testParseCustomizations_badEffectId_throwsException(
+ boolean hasConfigFile, boolean hasRes) throws Exception {
// Negative id
- assertParseCustomizationsFails(
- "<haptic-feedback-constants>"
+ String xmlNegativeId = "<haptic-feedback-constants>"
+ "<constant id=\"-10\">"
+ COMPOSITION_VIBRATION_XML
+ "</constant>"
- + "</haptic-feedback-constants>");
-
+ + "</haptic-feedback-constants>";
// Non-numeral id
- assertParseCustomizationsFails(
- "<haptic-feedback-constants>"
+ String xmlNonNumericalId = "<haptic-feedback-constants>"
+ "<constant id=\"xyz\">"
+ COMPOSITION_VIBRATION_XML
+ "</constant>"
- + "</haptic-feedback-constants>");
+ + "</haptic-feedback-constants>";
+
+ assertParseCustomizationsFails(xmlNegativeId, hasConfigFile, hasRes);
+ assertParseCustomizationsFails(xmlNonNumericalId, hasConfigFile, hasRes);
}
@Test
- public void testParseCustomizations_malformedXml_throwsException() throws Exception {
+ @Parameters(method = "hapticFeedbackCustomizationTestArguments")
+ public void testParseCustomizations_malformedXml_throwsException(
+ boolean hasConfigFile, boolean hasRes) throws Exception {
// No start "<constant>" tag
- assertParseCustomizationsFails(
- "<haptic-feedback-constants>"
+ String xmlNoStartConstantTag = "<haptic-feedback-constants>"
+ COMPOSITION_VIBRATION_XML
+ "</constant>"
- + "</haptic-feedback-constants>");
-
+ + "</haptic-feedback-constants>";
// No end "<constant>" tag
- assertParseCustomizationsFails(
- "<haptic-feedback-constants>"
+ String xmlNoEndConstantTag = "<haptic-feedback-constants>"
+ "<constant id=\"10\">"
+ COMPOSITION_VIBRATION_XML
- + "</haptic-feedback-constants>");
-
+ + "</haptic-feedback-constants>";
// No start "<haptic-feedback-constants>" tag
- assertParseCustomizationsFails(
- "<constant id=\"10\">"
+ String xmlNoStartCustomizationTag = "<constant id=\"10\">"
+ COMPOSITION_VIBRATION_XML
+ "</constant>"
- + "</haptic-feedback-constants>");
-
+ + "</haptic-feedback-constants>";
// No end "<haptic-feedback-constants>" tag
- assertParseCustomizationsFails(
- "<haptic-feedback-constants>"
+ String xmlNoEndCustomizationTag = "<haptic-feedback-constants>"
+ "<constant id=\"10\">"
+ COMPOSITION_VIBRATION_XML
- + "</constant>");
+ + "</constant>";
+
+ assertParseCustomizationsFails(xmlNoStartConstantTag, hasConfigFile, hasRes);
+ assertParseCustomizationsFails(xmlNoEndConstantTag, hasConfigFile, hasRes);
+ assertParseCustomizationsFails(xmlNoStartCustomizationTag, hasConfigFile, hasRes);
+ assertParseCustomizationsFails(xmlNoEndCustomizationTag, hasConfigFile, hasRes);
}
@Test
- public void testParseCustomizations_badVibrationXml_throwsException() throws Exception {
- assertParseCustomizationsFails(
- "<haptic-feedback-constants>"
+ @Parameters(method = "hapticFeedbackCustomizationTestArguments")
+ public void testParseCustomizations_badVibrationXml_throwsException(
+ boolean hasConfigFile, boolean hasRes) throws Exception {
+ String xmlBad1 = "<haptic-feedback-constants>"
+ "<constant id=\"10\">"
+ "<bad-vibration-effect></bad-vibration-effect>"
+ "</constant>"
- + "</haptic-feedback-constants>");
-
- assertParseCustomizationsFails(
- "<haptic-feedback-constants>"
+ + "</haptic-feedback-constants>";
+ String xmlBad2 = "<haptic-feedback-constants>"
+ "<constant id=\"10\">"
+ "<vibration-effect><predefined-effect name=\"bad-effect\"/></vibration-effect>"
+ "</constant>"
- + "</haptic-feedback-constants>");
-
- assertParseCustomizationsFails(
- "<haptic-feedback-constants>"
+ + "</haptic-feedback-constants>";
+ String xmlBad3 = "<haptic-feedback-constants>"
+ "<constant id=\"10\">"
+ "<vibration-select>"
+ "<vibration-effect><predefined-effect name=\"bad-effect\"/></vibration-effect>"
+ "</constant>"
- + "</haptic-feedback-constants>");
-
- assertParseCustomizationsFails(
- "<haptic-feedback-constants>"
+ + "</haptic-feedback-constants>";
+ String xmlBad4 = "<haptic-feedback-constants>"
+ "<constant id=\"10\">"
+ "<vibration-effect><predefined-effect name=\"bad-effect\"/></vibration-effect>"
+ "</vibration-select>"
+ "</constant>"
- + "</haptic-feedback-constants>");
+ + "</haptic-feedback-constants>";
+
+ assertParseCustomizationsFails(xmlBad1, hasConfigFile, hasRes);
+ assertParseCustomizationsFails(xmlBad2, hasConfigFile, hasRes);
+ assertParseCustomizationsFails(xmlBad3, hasConfigFile, hasRes);
+ assertParseCustomizationsFails(xmlBad4, hasConfigFile, hasRes);
}
@Test
- public void testParseCustomizations_badConstantAttribute_throwsException() throws Exception {
- assertParseCustomizationsFails(
- "<haptic-feedback-constants>"
+ @Parameters(method = "hapticFeedbackCustomizationTestArguments")
+ public void testParseCustomizations_badConstantAttribute_throwsException(
+ boolean hasConfigFile, boolean hasRes) throws Exception {
+ String xmlBadConstantAttribute1 = "<haptic-feedback-constants>"
+ "<constant iddddd=\"10\">"
+ COMPOSITION_VIBRATION_XML
+ "</constant>"
- + "</haptic-feedback-constants>");
-
- assertParseCustomizationsFails(
- "<haptic-feedback-constants>"
+ + "</haptic-feedback-constants>";
+ String xmlBadConstantAttribute2 = "<haptic-feedback-constants>"
+ "<constant id=\"10\" unwanted-attr=\"1\">"
+ COMPOSITION_VIBRATION_XML
+ "</constant>"
- + "</haptic-feedback-constants>");
+ + "</haptic-feedback-constants>";
+
+ assertParseCustomizationsFails(xmlBadConstantAttribute1, hasConfigFile, hasRes);
+ assertParseCustomizationsFails(xmlBadConstantAttribute2, hasConfigFile, hasRes);
}
@Test
- public void testParseCustomizations_duplicateEffects_throwsException() throws Exception {
- assertParseCustomizationsFails(
- "<haptic-feedback-constants>"
+ @Parameters(method = "hapticFeedbackCustomizationTestArguments")
+ public void testParseCustomizations_duplicateEffects_throwsException(
+ boolean hasConfigFile, boolean hasRes) throws Exception {
+ String xmlDuplicateEffect = "<haptic-feedback-constants>"
+ "<constant id=\"10\">"
+ COMPOSITION_VIBRATION_XML
+ "</constant>"
@@ -397,30 +452,44 @@ public class HapticFeedbackCustomizationTest {
+ "<constant id=\"11\">"
+ PREDEFINED_VIBRATION_XML
+ "</constant>"
- + "</haptic-feedback-constants>");
+ + "</haptic-feedback-constants>";
+
+ assertParseCustomizationsFails(xmlDuplicateEffect, hasConfigFile, hasRes);
}
- private void assertParseCustomizationsSucceeds(
- String xml, SparseArray<VibrationEffect> expectedCustomizations) throws Exception {
- setupCustomizationFile(xml);
+ private void assertParseCustomizationsSucceeds(String xml,
+ SparseArray<VibrationEffect> expectedCustomizations, boolean hasConfigFile,
+ boolean hasRes) throws Exception {
+ setupParseCustomizations(xml, hasConfigFile, hasRes);
assertThat(expectedCustomizations.contentEquals(
HapticFeedbackCustomization.loadVibrations(mResourcesMock, mVibratorInfoMock)))
- .isTrue();
+ .isTrue();
}
- private void assertParseCustomizationsFails(String xml) throws Exception {
- setupCustomizationFile(xml);
- assertThrows("Expected haptic feedback customization to fail for " + xml,
+ private void assertParseCustomizationsFails(String xml, boolean hasConfigFile, boolean hasRes)
+ throws Exception {
+ setupParseCustomizations(xml, hasConfigFile, hasRes);
+ assertThrows("Expected haptic feedback customization to fail",
CustomizationParserException.class,
() -> HapticFeedbackCustomization.loadVibrations(
mResourcesMock, mVibratorInfoMock));
}
- private void assertParseCustomizationsFails() throws Exception {
- assertThrows("Expected haptic feedback customization to fail",
- CustomizationParserException.class,
- () -> HapticFeedbackCustomization.loadVibrations(
- mResourcesMock, mVibratorInfoMock));
+ private void setupParseCustomizations(String xml, boolean hasConfigFile, boolean hasRes)
+ throws Exception {
+ clearFileAndResourceSetup();
+ if (hasConfigFile) {
+ setupCustomizationFile(xml);
+ }
+ if (hasRes) {
+ setupCustomizationResource(xml);
+ }
+ }
+
+ private void clearFileAndResourceSetup() {
+ when(mResourcesMock.getString(R.string.config_hapticFeedbackCustomizationFile))
+ .thenReturn(null);
+ when(mResourcesMock.getXml(haptic_feedback_customization)).thenReturn(null);
}
private void setupCustomizationFile(String xml) throws Exception {
@@ -433,6 +502,13 @@ public class HapticFeedbackCustomizationTest {
.thenReturn(path);
}
+ private void setupCustomizationResource(String xml) throws Exception {
+ mSetFlagsRule.enableFlags(
+ Flags.FLAG_LOAD_HAPTIC_FEEDBACK_VIBRATION_CUSTOMIZATION_FROM_RESOURCES);
+ when(mResourcesMock.getXml(haptic_feedback_customization))
+ .thenReturn(FakeXmlResourceParser.fromXml(xml));
+ }
+
private void makeSupported(VibrationEffect... effects) {
for (VibrationEffect effect : effects) {
when(mVibratorInfoMock.areVibrationFeaturesSupported(effect)).thenReturn(true);
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/HapticFeedbackVibrationProviderTest.java b/services/tests/vibrator/src/com/android/server/vibrator/HapticFeedbackVibrationProviderTest.java
index 4f7593184d83..240bd1ec56e3 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/HapticFeedbackVibrationProviderTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/HapticFeedbackVibrationProviderTest.java
@@ -50,7 +50,6 @@ import android.hardware.vibrator.IVibrator;
import android.os.VibrationAttributes;
import android.os.VibrationEffect;
import android.os.VibratorInfo;
-import android.os.vibrator.Flags;
import android.platform.test.flag.junit.SetFlagsRule;
import android.util.AtomicFile;
import android.util.SparseArray;
@@ -256,22 +255,7 @@ public class HapticFeedbackVibrationProviderTest {
}
@Test
- public void testKeyboardHaptic_fixAmplitude_keyboardCategoryOff_defaultVibrationReturned() {
- mSetFlagsRule.disableFlags(Flags.FLAG_KEYBOARD_CATEGORY_ENABLED);
- mockVibratorPrimitiveSupport(PRIMITIVE_CLICK, PRIMITIVE_TICK);
- mockKeyboardVibrationFixedAmplitude(KEYBOARD_VIBRATION_FIXED_AMPLITUDE);
-
- HapticFeedbackVibrationProvider hapticProvider = createProviderWithDefaultCustomizations();
-
- assertThat(hapticProvider.getVibrationForHapticFeedback(KEYBOARD_TAP))
- .isEqualTo(VibrationEffect.get(EFFECT_CLICK, true /* fallback */));
- assertThat(hapticProvider.getVibrationForHapticFeedback(KEYBOARD_RELEASE))
- .isEqualTo(VibrationEffect.get(EFFECT_TICK, false /* fallback */));
- }
-
- @Test
public void testKeyboardHaptic_fixAmplitude_keyboardCategoryOn_keyboardVibrationReturned() {
- mSetFlagsRule.enableFlags(Flags.FLAG_KEYBOARD_CATEGORY_ENABLED);
mockVibratorPrimitiveSupport(PRIMITIVE_CLICK, PRIMITIVE_TICK);
mockKeyboardVibrationFixedAmplitude(KEYBOARD_VIBRATION_FIXED_AMPLITUDE);
@@ -346,24 +330,7 @@ public class HapticFeedbackVibrationProviderTest {
}
@Test
- public void testVibrationAttribute_keyboardCategoryOff_isIme_useTouchUsage() {
- mSetFlagsRule.disableFlags(Flags.FLAG_KEYBOARD_CATEGORY_ENABLED);
- HapticFeedbackVibrationProvider hapticProvider = createProviderWithDefaultCustomizations();
-
- for (int effectId : KEYBOARD_FEEDBACK_CONSTANTS) {
- VibrationAttributes attrs = hapticProvider.getVibrationAttributesForHapticFeedback(
- effectId, /* flags */ 0,
- HapticFeedbackConstants.PRIVATE_FLAG_APPLY_INPUT_METHOD_SETTINGS);
- assertWithMessage("Expected USAGE_TOUCH for effect " + effectId)
- .that(attrs.getUsage()).isEqualTo(USAGE_TOUCH);
- assertWithMessage("Expected no CATEGORY_KEYBOARD for effect " + effectId)
- .that(attrs.getCategory()).isEqualTo(CATEGORY_UNKNOWN);
- }
- }
-
- @Test
public void testVibrationAttribute_keyboardCategoryOn_notIme_useTouchUsage() {
- mSetFlagsRule.enableFlags(Flags.FLAG_KEYBOARD_CATEGORY_ENABLED);
HapticFeedbackVibrationProvider hapticProvider = createProviderWithDefaultCustomizations();
for (int effectId : KEYBOARD_FEEDBACK_CONSTANTS) {
@@ -378,7 +345,6 @@ public class HapticFeedbackVibrationProviderTest {
@Test
public void testVibrationAttribute_keyboardCategoryOn_isIme_useImeFeedbackUsage() {
- mSetFlagsRule.enableFlags(Flags.FLAG_KEYBOARD_CATEGORY_ENABLED);
HapticFeedbackVibrationProvider hapticProvider = createProviderWithDefaultCustomizations();
for (int effectId : KEYBOARD_FEEDBACK_CONSTANTS) {
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibrationSettingsTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibrationSettingsTest.java
index 8d4a6aa5ba29..72ef888aa061 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/VibrationSettingsTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/VibrationSettingsTest.java
@@ -70,9 +70,7 @@ import android.os.VibrationAttributes;
import android.os.VibrationEffect;
import android.os.Vibrator;
import android.os.test.TestLooper;
-import android.os.vibrator.Flags;
import android.os.vibrator.VibrationConfig;
-import android.platform.test.annotations.RequiresFlagsEnabled;
import android.platform.test.flag.junit.CheckFlagsRule;
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.provider.Settings;
@@ -602,7 +600,6 @@ public class VibrationSettingsTest {
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_KEYBOARD_CATEGORY_ENABLED)
public void shouldIgnoreVibration_withKeyboardSettingsOff_shouldIgnoreKeyboardVibration() {
setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_MEDIUM);
setUserSetting(Settings.System.KEYBOARD_VIBRATION_ENABLED, 0 /* OFF*/);
@@ -627,7 +624,6 @@ public class VibrationSettingsTest {
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_KEYBOARD_CATEGORY_ENABLED)
public void shouldIgnoreVibration_withKeyboardSettingsOn_shouldNotIgnoreKeyboardVibration() {
setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_OFF);
setUserSetting(Settings.System.KEYBOARD_VIBRATION_ENABLED, 1 /* ON */);
@@ -645,7 +641,6 @@ public class VibrationSettingsTest {
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_KEYBOARD_CATEGORY_ENABLED)
public void shouldIgnoreVibration_notSupportKeyboardVibration_ignoresKeyboardTouchVibration() {
setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_OFF);
setUserSetting(Settings.System.KEYBOARD_VIBRATION_ENABLED, 1 /* ON */);
diff --git a/services/tests/vibrator/utils/com/android/server/vibrator/FakeXmlResourceParser.java b/services/tests/vibrator/utils/com/android/server/vibrator/FakeXmlResourceParser.java
new file mode 100644
index 000000000000..ab7d43c66765
--- /dev/null
+++ b/services/tests/vibrator/utils/com/android/server/vibrator/FakeXmlResourceParser.java
@@ -0,0 +1,330 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.vibrator;
+
+import android.content.res.XmlResourceParser;
+import android.util.Xml;
+
+import com.android.modules.utils.TypedXmlPullParser;
+
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.BufferedInputStream;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.Reader;
+
+/**
+ * Wrapper to use TypedXmlPullParser as XmlResourceParser for Resources.getXml(). This is borrowed
+ * from {@code ZenModeHelperTest}.
+ */
+public final class FakeXmlResourceParser implements XmlResourceParser {
+ private final TypedXmlPullParser mParser;
+
+ public FakeXmlResourceParser(TypedXmlPullParser parser) {
+ this.mParser = parser;
+ }
+
+ /** Create a {@link FakeXmlResourceParser} given a xml {@link String}. */
+ public static XmlResourceParser fromXml(String xml) throws XmlPullParserException {
+ TypedXmlPullParser parser = Xml.newFastPullParser();
+ parser.setInput(new BufferedInputStream(new ByteArrayInputStream(xml.getBytes())), null);
+ return new FakeXmlResourceParser(parser);
+ }
+
+ @Override
+ public int getEventType() throws XmlPullParserException {
+ return mParser.getEventType();
+ }
+
+ @Override
+ public void setFeature(String name, boolean state) throws XmlPullParserException {
+ mParser.setFeature(name, state);
+ }
+
+ @Override
+ public boolean getFeature(String name) {
+ return false;
+ }
+
+ @Override
+ public void setProperty(String name, Object value) throws XmlPullParserException {
+ mParser.setProperty(name, value);
+ }
+
+ @Override
+ public Object getProperty(String name) {
+ return mParser.getProperty(name);
+ }
+
+ @Override
+ public void setInput(Reader in) throws XmlPullParserException {
+ mParser.setInput(in);
+ }
+
+ @Override
+ public void setInput(InputStream inputStream, String inputEncoding)
+ throws XmlPullParserException {
+ mParser.setInput(inputStream, inputEncoding);
+ }
+
+ @Override
+ public String getInputEncoding() {
+ return mParser.getInputEncoding();
+ }
+
+ @Override
+ public void defineEntityReplacementText(String entityName, String replacementText)
+ throws XmlPullParserException {
+ mParser.defineEntityReplacementText(entityName, replacementText);
+ }
+
+ @Override
+ public int getNamespaceCount(int depth) throws XmlPullParserException {
+ return mParser.getNamespaceCount(depth);
+ }
+
+ @Override
+ public String getNamespacePrefix(int pos) throws XmlPullParserException {
+ return mParser.getNamespacePrefix(pos);
+ }
+
+ @Override
+ public String getNamespaceUri(int pos) throws XmlPullParserException {
+ return mParser.getNamespaceUri(pos);
+ }
+
+ @Override
+ public String getNamespace(String prefix) {
+ return mParser.getNamespace(prefix);
+ }
+
+ @Override
+ public int getDepth() {
+ return mParser.getDepth();
+ }
+
+ @Override
+ public String getPositionDescription() {
+ return mParser.getPositionDescription();
+ }
+
+ @Override
+ public int getLineNumber() {
+ return mParser.getLineNumber();
+ }
+
+ @Override
+ public int getColumnNumber() {
+ return mParser.getColumnNumber();
+ }
+
+ @Override
+ public boolean isWhitespace() throws XmlPullParserException {
+ return mParser.isWhitespace();
+ }
+
+ @Override
+ public String getText() {
+ return mParser.getText();
+ }
+
+ @Override
+ public char[] getTextCharacters(int[] holderForStartAndLength) {
+ return mParser.getTextCharacters(holderForStartAndLength);
+ }
+
+ @Override
+ public String getNamespace() {
+ return mParser.getNamespace();
+ }
+
+ @Override
+ public String getName() {
+ return mParser.getName();
+ }
+
+ @Override
+ public String getPrefix() {
+ return mParser.getPrefix();
+ }
+
+ @Override
+ public boolean isEmptyElementTag() throws XmlPullParserException {
+ return false;
+ }
+
+ @Override
+ public int getAttributeCount() {
+ return mParser.getAttributeCount();
+ }
+
+ @Override
+ public int next() throws IOException, XmlPullParserException {
+ return mParser.next();
+ }
+
+ @Override
+ public int nextToken() throws XmlPullParserException, IOException {
+ return mParser.next();
+ }
+
+ @Override
+ public void require(int type, String namespace, String name)
+ throws XmlPullParserException, IOException {
+ mParser.require(type, namespace, name);
+ }
+
+ @Override
+ public String nextText() throws XmlPullParserException, IOException {
+ return mParser.nextText();
+ }
+
+ @Override
+ public String getAttributeNamespace(int index) {
+ return "";
+ }
+
+ @Override
+ public String getAttributeName(int index) {
+ return mParser.getAttributeName(index);
+ }
+
+ @Override
+ public String getAttributePrefix(int index) {
+ return mParser.getAttributePrefix(index);
+ }
+
+ @Override
+ public String getAttributeType(int index) {
+ return mParser.getAttributeType(index);
+ }
+
+ @Override
+ public boolean isAttributeDefault(int index) {
+ return mParser.isAttributeDefault(index);
+ }
+
+ @Override
+ public String getAttributeValue(int index) {
+ return mParser.getAttributeValue(index);
+ }
+
+ @Override
+ public String getAttributeValue(String namespace, String name) {
+ return mParser.getAttributeValue(namespace, name);
+ }
+
+ @Override
+ public int getAttributeNameResource(int index) {
+ return 0;
+ }
+
+ @Override
+ public int getAttributeListValue(String namespace, String attribute, String[] options,
+ int defaultValue) {
+ return 0;
+ }
+
+ @Override
+ public boolean getAttributeBooleanValue(String namespace, String attribute,
+ boolean defaultValue) {
+ return false;
+ }
+
+ @Override
+ public int getAttributeResourceValue(String namespace, String attribute, int defaultValue) {
+ return 0;
+ }
+
+ @Override
+ public int getAttributeIntValue(String namespace, String attribute, int defaultValue) {
+ return 0;
+ }
+
+ @Override
+ public int getAttributeUnsignedIntValue(String namespace, String attribute,
+ int defaultValue) {
+ return 0;
+ }
+
+ @Override
+ public float getAttributeFloatValue(String namespace, String attribute,
+ float defaultValue) {
+ return 0;
+ }
+
+ @Override
+ public int getAttributeListValue(int index, String[] options, int defaultValue) {
+ return 0;
+ }
+
+ @Override
+ public boolean getAttributeBooleanValue(int index, boolean defaultValue) {
+ return false;
+ }
+
+ @Override
+ public int getAttributeResourceValue(int index, int defaultValue) {
+ return 0;
+ }
+
+ @Override
+ public int getAttributeIntValue(int index, int defaultValue) {
+ return 0;
+ }
+
+ @Override
+ public int getAttributeUnsignedIntValue(int index, int defaultValue) {
+ return 0;
+ }
+
+ @Override
+ public float getAttributeFloatValue(int index, float defaultValue) {
+ return 0;
+ }
+
+ @Override
+ public String getIdAttribute() {
+ return null;
+ }
+
+ @Override
+ public String getClassAttribute() {
+ return null;
+ }
+
+ @Override
+ public int getIdAttributeResourceValue(int defaultValue) {
+ return 0;
+ }
+
+ @Override
+ public int getStyleAttribute() {
+ return 0;
+ }
+
+ @Override
+ public void close() {
+ }
+
+ @Override
+ public int nextTag() throws IOException, XmlPullParserException {
+ return mParser.nextTag();
+ }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/FrameRateSelectionPriorityTests.java b/services/tests/wmtests/src/com/android/server/wm/FrameRateSelectionPriorityTests.java
index e77c14a60179..eacb8e9d628d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/FrameRateSelectionPriorityTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/FrameRateSelectionPriorityTests.java
@@ -82,12 +82,15 @@ public class FrameRateSelectionPriorityTests extends WindowTestsBase {
public void setUp() {
DisplayInfo di = new DisplayInfo(mDisplayInfo);
Mode defaultMode = di.getDefaultMode();
- di.supportedModes = new Mode[] {
- new Mode(1, defaultMode.getPhysicalWidth(), defaultMode.getPhysicalHeight(), 90),
- new Mode(2, defaultMode.getPhysicalWidth(), defaultMode.getPhysicalHeight(), 70),
- new Mode(LOW_MODE_ID,
- defaultMode.getPhysicalWidth(), defaultMode.getPhysicalHeight(), 60),
- };
+ Mode hiMode = new Mode(1,
+ defaultMode.getPhysicalWidth(), defaultMode.getPhysicalHeight(), 90);
+ Mode midMode = new Mode(2,
+ defaultMode.getPhysicalWidth(), defaultMode.getPhysicalHeight(), 70);
+ Mode lowMode = new Mode(LOW_MODE_ID,
+ defaultMode.getPhysicalWidth(), defaultMode.getPhysicalHeight(), 60);
+
+ di.supportedModes = new Mode[] { hiMode, midMode };
+ di.appsSupportedModes = new Mode[] { hiMode, midMode, lowMode };
di.defaultModeId = 1;
mRefreshRatePolicy = new RefreshRatePolicy(mWm, di, mDenylist);
when(mDisplayPolicy.getRefreshRatePolicy()).thenReturn(mRefreshRatePolicy);
diff --git a/services/tests/wmtests/src/com/android/server/wm/RefreshRatePolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/RefreshRatePolicyTest.java
index 7ebf9ac324d5..3fa38bfe7185 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RefreshRatePolicyTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RefreshRatePolicyTest.java
@@ -66,7 +66,6 @@ public class RefreshRatePolicyTest extends WindowTestsBase {
private RefreshRatePolicy mPolicy;
private HighRefreshRateDenylist mDenylist = mock(HighRefreshRateDenylist.class);
- private FrameRateVote mTempFrameRateVote = new FrameRateVote();
private static final FrameRateVote FRAME_RATE_VOTE_NONE = new FrameRateVote();
private static final FrameRateVote FRAME_RATE_VOTE_DENY_LIST =
@@ -98,18 +97,14 @@ public class RefreshRatePolicyTest extends WindowTestsBase {
@Before
public void setUp() {
Mode defaultMode = mDisplayInfo.getDefaultMode();
- mDisplayInfo.supportedModes = new Mode[] {
- new Mode(HI_MODE_ID,
- defaultMode.getPhysicalWidth(), defaultMode.getPhysicalHeight(),
- HI_REFRESH_RATE),
- new Mode(MID_MODE_ID,
- defaultMode.getPhysicalWidth(), defaultMode.getPhysicalHeight(),
- MID_REFRESH_RATE),
- new Mode(LOW_MODE_ID,
- defaultMode.getPhysicalWidth(), defaultMode.getPhysicalHeight(),
- LOW_REFRESH_RATE),
- };
- mDisplayInfo.appsSupportedModes = mDisplayInfo.supportedModes;
+ Mode hiMode = new Mode(HI_MODE_ID,
+ defaultMode.getPhysicalWidth(), defaultMode.getPhysicalHeight(), HI_REFRESH_RATE);
+ Mode midMode = new Mode(MID_MODE_ID,
+ defaultMode.getPhysicalWidth(), defaultMode.getPhysicalHeight(), MID_REFRESH_RATE);
+ Mode lowMode = new Mode(LOW_MODE_ID,
+ defaultMode.getPhysicalWidth(), defaultMode.getPhysicalHeight(), LOW_REFRESH_RATE);
+ mDisplayInfo.supportedModes = new Mode[] { hiMode, midMode };
+ mDisplayInfo.appsSupportedModes = new Mode[] { hiMode, midMode, lowMode };
mDisplayInfo.defaultModeId = HI_MODE_ID;
mPolicy = new RefreshRatePolicy(mWm, mDisplayInfo, mDenylist);
}
diff --git a/telephony/java/android/service/euicc/EuiccService.java b/telephony/java/android/service/euicc/EuiccService.java
index 55245419c570..a01a72003570 100644
--- a/telephony/java/android/service/euicc/EuiccService.java
+++ b/telephony/java/android/service/euicc/EuiccService.java
@@ -267,6 +267,17 @@ public abstract class EuiccService extends Service {
"android.service.euicc.extra.RESOLUTION_CONFIRMATION_CODE_RETRIED";
/**
+ * Bundle key for the {@code resolvedBundle} passed to {@link #onDownloadSubscription(
+ * int, int, DownloadableSubscription, boolean, boolean, Bundle)}. The value is a
+ * {@link String} for the package name of the app calling the
+ * {@link EuiccManager#downloadSubscription(int, DownloadableSubscription, PendingIntent)} API.
+ * This is to be used by LPA to determine the app that is requesting the download.
+ *
+ * @hide
+ */
+ public static final String EXTRA_PACKAGE_NAME = "android.service.euicc.extra.PACKAGE_NAME";
+
+ /**
* Intent extra set for resolution requests containing an int indicating the current card Id.
*/
public static final String EXTRA_RESOLUTION_CARD_ID =
diff --git a/test-mock/Android.bp b/test-mock/Android.bp
index 59766579eee2..71f303311047 100644
--- a/test-mock/Android.bp
+++ b/test-mock/Android.bp
@@ -47,6 +47,10 @@ java_sdk_library {
compile_dex: true,
default_to_stubs: true,
dist_group: "android",
+
+ // This module cannot generate stubs from the api signature files as stubs depends on the
+ // private APIs, which are not visible in the api signature files.
+ build_from_text_stub: false,
}
java_library {