Merge "Change IAutofillManagerClient to IBinder for getCandidateCreds API" into main
diff --git a/Ravenwood.bp b/Ravenwood.bp
index f330ad1..d13c4d7 100644
--- a/Ravenwood.bp
+++ b/Ravenwood.bp
@@ -75,16 +75,35 @@
],
}
+java_library {
+ name: "mockito-ravenwood-prebuilt",
+ installable: false,
+ static_libs: [
+ "mockito-robolectric-prebuilt",
+ ],
+}
+
+java_library {
+ name: "inline-mockito-ravenwood-prebuilt",
+ installable: false,
+ static_libs: [
+ "inline-mockito-robolectric-prebuilt",
+ ],
+}
+
android_ravenwood_libgroup {
name: "ravenwood-runtime",
libs: [
"framework-minus-apex.ravenwood",
"hoststubgen-helper-runtime.ravenwood",
"hoststubgen-helper-framework-runtime.ravenwood",
+ "all-updatable-modules-system-stubs",
"junit",
"truth",
"ravenwood-junit-impl",
"android.test.mock.ravenwood",
+ "mockito-ravenwood-prebuilt",
+ "inline-mockito-ravenwood-prebuilt",
],
}
@@ -94,5 +113,7 @@
"junit",
"truth",
"ravenwood-junit",
+ "mockito-ravenwood-prebuilt",
+ "inline-mockito-ravenwood-prebuilt",
],
}
diff --git a/TEST_MAPPING b/TEST_MAPPING
index ecfd86c..d59775f 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -138,14 +138,15 @@
}
],
"postsubmit-ravenwood": [
- {
- "name": "CtsUtilTestCasesRavenwood",
- "host": true,
- "file_patterns": [
- "*Ravenwood*",
- "*ravenwood*"
- ]
- }
+ // TODO(ravenwood) promote it to presubmit
+ // TODO: Enable it once the infra knows how to run it.
+// {
+// "name": "CtsUtilTestCasesRavenwood",
+// "file_patterns": [
+// "*Ravenwood*",
+// "*ravenwood*"
+// ]
+// }
],
"postsubmit-managedprofile-stress": [
{
diff --git a/api/StubLibraries.bp b/api/StubLibraries.bp
index 74344cd..59c0128 100644
--- a/api/StubLibraries.bp
+++ b/api/StubLibraries.bp
@@ -239,6 +239,10 @@
name: "android-non-updatable_from_source_defaults",
libs: ["stub-annotations"],
static_libs: ["framework-res-package-jar"], // Export package of framework-res
+}
+
+java_defaults {
+ name: "android-non-updatable_exportable_from_source_defaults",
dist: {
targets: ["sdk"],
tag: ".jar",
@@ -265,6 +269,14 @@
}
java_library {
+ name: "android-non-updatable.stubs.exportable",
+ defaults: ["android-non-updatable_defaults"],
+ static_libs: [
+ "android-non-updatable.stubs.exportable.from-source",
+ ],
+}
+
+java_library {
name: "android-non-updatable.stubs.system",
defaults: ["android-non-updatable_defaults"],
static_libs: [
@@ -283,6 +295,14 @@
}
java_library {
+ name: "android-non-updatable.stubs.exportable.system",
+ defaults: ["android-non-updatable_defaults"],
+ static_libs: [
+ "android-non-updatable.stubs.exportable.system.from-source",
+ ],
+}
+
+java_library {
name: "android-non-updatable.stubs.module_lib",
defaults: ["android-non-updatable_defaults"],
static_libs: [
@@ -301,6 +321,14 @@
}
java_library {
+ name: "android-non-updatable.stubs.exportable.module_lib",
+ defaults: ["android-non-updatable_defaults"],
+ static_libs: [
+ "android-non-updatable.stubs.exportable.module_lib.from-source",
+ ],
+}
+
+java_library {
name: "android-non-updatable.stubs.test",
defaults: ["android-non-updatable_defaults"],
static_libs: [
@@ -319,6 +347,14 @@
}
java_library {
+ name: "android-non-updatable.stubs.exportable.test",
+ defaults: ["android-non-updatable_defaults"],
+ static_libs: [
+ "android-non-updatable.stubs.exportable.test.from-source",
+ ],
+}
+
+java_library {
name: "android-non-updatable.stubs.from-source",
defaults: [
"android-non-updatable_defaults",
@@ -326,6 +362,17 @@
],
srcs: [":api-stubs-docs-non-updatable"],
libs: ["all-modules-public-stubs"],
+}
+
+java_library {
+ name: "android-non-updatable.stubs.exportable.from-source",
+ defaults: [
+ "android-non-updatable_defaults",
+ "android-non-updatable_from_source_defaults",
+ "android-non-updatable_exportable_from_source_defaults",
+ ],
+ srcs: [":api-stubs-docs-non-updatable{.exportable}"],
+ libs: ["all-modules-public-stubs"],
dist: {
dir: "apistubs/android/public",
},
@@ -339,6 +386,17 @@
],
srcs: [":system-api-stubs-docs-non-updatable"],
libs: ["all-modules-system-stubs"],
+}
+
+java_library {
+ name: "android-non-updatable.stubs.exportable.system.from-source",
+ defaults: [
+ "android-non-updatable_defaults",
+ "android-non-updatable_from_source_defaults",
+ "android-non-updatable_exportable_from_source_defaults",
+ ],
+ srcs: [":system-api-stubs-docs-non-updatable{.exportable}"],
+ libs: ["all-modules-system-stubs"],
dist: {
dir: "apistubs/android/system",
},
@@ -352,6 +410,17 @@
],
srcs: [":module-lib-api-stubs-docs-non-updatable"],
libs: non_updatable_api_deps_on_modules,
+}
+
+java_library {
+ name: "android-non-updatable.stubs.exportable.module_lib.from-source",
+ defaults: [
+ "android-non-updatable_defaults",
+ "android-non-updatable_from_source_defaults",
+ "android-non-updatable_exportable_from_source_defaults",
+ ],
+ srcs: [":module-lib-api-stubs-docs-non-updatable{.exportable}"],
+ libs: non_updatable_api_deps_on_modules,
dist: {
dir: "apistubs/android/module-lib",
},
@@ -365,6 +434,17 @@
],
srcs: [":test-api-stubs-docs-non-updatable"],
libs: ["all-modules-system-stubs"],
+}
+
+java_library {
+ name: "android-non-updatable.stubs.exportable.test.from-source",
+ defaults: [
+ "android-non-updatable_defaults",
+ "android-non-updatable_from_source_defaults",
+ "android-non-updatable_exportable_from_source_defaults",
+ ],
+ srcs: [":test-api-stubs-docs-non-updatable{.exportable}"],
+ libs: ["all-modules-system-stubs"],
dist: {
dir: "apistubs/android/test",
},
@@ -462,6 +542,16 @@
}
java_library {
+ name: "android_stubs_current_exportable.from-source",
+ static_libs: [
+ "all-modules-public-stubs-exportable",
+ "android-non-updatable.stubs.exportable",
+ "private-stub-annotations-jar",
+ ],
+ defaults: ["android.jar_defaults"],
+}
+
+java_library {
name: "android_system_stubs_current.from-source",
static_libs: [
"all-modules-system-stubs",
@@ -470,6 +560,19 @@
],
defaults: [
"android.jar_defaults",
+ ],
+ visibility: ["//frameworks/base/services"],
+}
+
+java_library {
+ name: "android_system_stubs_current_exportable.from-source",
+ static_libs: [
+ "all-modules-system-stubs-exportable",
+ "android-non-updatable.stubs.exportable.system",
+ "private-stub-annotations-jar",
+ ],
+ defaults: [
+ "android.jar_defaults",
"android_stubs_dists_default",
],
dist: {
@@ -498,6 +601,23 @@
],
defaults: [
"android.jar_defaults",
+ ],
+ visibility: ["//frameworks/base/services"],
+}
+
+java_library {
+ name: "android_test_stubs_current_exportable.from-source",
+ 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.
+ "all-updatable-modules-system-stubs-exportable",
+ // Non-updatable modules on the other hand can have test APIs, so include their test-stubs.
+ "all-non-updatable-modules-test-stubs-exportable",
+ "android-non-updatable.stubs.exportable.test",
+ "private-stub-annotations-jar",
+ ],
+ defaults: [
+ "android.jar_defaults",
"android_stubs_dists_default",
],
dist: {
@@ -505,6 +625,7 @@
},
}
+// This module does not need to be copied to dist
java_library {
name: "android_test_frameworks_core_stubs_current.from-source",
static_libs: [
@@ -513,24 +634,34 @@
],
defaults: [
"android.jar_defaults",
- "android_stubs_dists_default",
],
- dist: {
- dir: "apistubs/android/test-core",
- },
+ visibility: ["//frameworks/base/services"],
}
java_library {
name: "android_module_lib_stubs_current.from-source",
defaults: [
"android.jar_defaults",
- "android_stubs_dists_default",
],
static_libs: [
"android-non-updatable.stubs.module_lib",
"art.module.public.api.stubs.module_lib",
"i18n.module.public.api.stubs",
],
+ visibility: ["//frameworks/base/services"],
+}
+
+java_library {
+ name: "android_module_lib_stubs_current_exportable.from-source",
+ defaults: [
+ "android.jar_defaults",
+ "android_stubs_dists_default",
+ ],
+ static_libs: [
+ "android-non-updatable.stubs.exportable.module_lib",
+ "art.module.public.api.stubs.exportable.module_lib",
+ "i18n.module.public.api.stubs.exportable",
+ ],
dist: {
dir: "apistubs/android/module-lib",
},
@@ -540,13 +671,26 @@
name: "android_system_server_stubs_current.from-source",
defaults: [
"android.jar_defaults",
- "android_stubs_dists_default",
],
srcs: [":services-non-updatable-stubs"],
installable: false,
static_libs: [
"android_module_lib_stubs_current.from-source",
],
+ visibility: ["//frameworks/base/services"],
+}
+
+java_library {
+ name: "android_system_server_stubs_current_exportable.from-source",
+ defaults: [
+ "android.jar_defaults",
+ "android_stubs_dists_default",
+ ],
+ srcs: [":services-non-updatable-stubs{.exportable}"],
+ installable: false,
+ static_libs: [
+ "android_module_lib_stubs_current_exportable.from-source",
+ ],
dist: {
dir: "apistubs/android/system-server",
},
diff --git a/api/api.go b/api/api.go
index b975c55..fa2be21 100644
--- a/api/api.go
+++ b/api/api.go
@@ -204,6 +204,15 @@
ctx.CreateModule(java.LibraryFactory, &props)
}
+func createMergedPublicExportableStubs(ctx android.LoadHookContext, modules []string) {
+ props := libraryProps{}
+ props.Name = proptools.StringPtr("all-modules-public-stubs-exportable")
+ props.Static_libs = transformArray(modules, "", ".stubs.exportable")
+ props.Sdk_version = proptools.StringPtr("module_current")
+ props.Visibility = []string{"//frameworks/base"}
+ ctx.CreateModule(java.LibraryFactory, &props)
+}
+
func createMergedSystemStubs(ctx android.LoadHookContext, modules []string) {
// First create the all-updatable-modules-system-stubs
{
@@ -228,6 +237,30 @@
}
}
+func createMergedSystemExportableStubs(ctx android.LoadHookContext, modules []string) {
+ // First create the all-updatable-modules-system-stubs
+ {
+ updatable_modules := removeAll(modules, non_updatable_modules)
+ props := libraryProps{}
+ props.Name = proptools.StringPtr("all-updatable-modules-system-stubs-exportable")
+ props.Static_libs = transformArray(updatable_modules, "", ".stubs.exportable.system")
+ props.Sdk_version = proptools.StringPtr("module_current")
+ props.Visibility = []string{"//frameworks/base"}
+ ctx.CreateModule(java.LibraryFactory, &props)
+ }
+ // Now merge all-updatable-modules-system-stubs and stubs from non-updatable modules
+ // into all-modules-system-stubs.
+ {
+ props := libraryProps{}
+ props.Name = proptools.StringPtr("all-modules-system-stubs-exportable")
+ props.Static_libs = transformArray(non_updatable_modules, "", ".stubs.exportable.system")
+ props.Static_libs = append(props.Static_libs, "all-updatable-modules-system-stubs-exportable")
+ props.Sdk_version = proptools.StringPtr("module_current")
+ props.Visibility = []string{"//frameworks/base"}
+ ctx.CreateModule(java.LibraryFactory, &props)
+ }
+}
+
func createMergedTestStubsForNonUpdatableModules(ctx android.LoadHookContext) {
props := libraryProps{}
props.Name = proptools.StringPtr("all-non-updatable-modules-test-stubs")
@@ -237,6 +270,15 @@
ctx.CreateModule(java.LibraryFactory, &props)
}
+func createMergedTestExportableStubsForNonUpdatableModules(ctx android.LoadHookContext) {
+ props := libraryProps{}
+ props.Name = proptools.StringPtr("all-non-updatable-modules-test-stubs-exportable")
+ props.Static_libs = transformArray(non_updatable_modules, "", ".stubs.exportable.test")
+ props.Sdk_version = proptools.StringPtr("module_current")
+ props.Visibility = []string{"//frameworks/base"}
+ ctx.CreateModule(java.LibraryFactory, &props)
+}
+
func createMergedFrameworkImpl(ctx android.LoadHookContext, modules []string) {
// This module is for the "framework-all" module, which should not include the core libraries.
modules = removeAll(modules, core_libraries_modules)
@@ -267,6 +309,19 @@
}
}
+func createMergedFrameworkModuleLibExportableStubs(ctx android.LoadHookContext, modules []string) {
+ // The user of this module compiles against the "core" SDK and against non-updatable modules,
+ // so remove to avoid dupes.
+ modules = removeAll(modules, core_libraries_modules)
+ modules = removeAll(modules, non_updatable_modules)
+ props := libraryProps{}
+ props.Name = proptools.StringPtr("framework-updatable-stubs-module_libs_api-exportable")
+ props.Static_libs = transformArray(modules, "", ".stubs.exportable.module_lib")
+ props.Sdk_version = proptools.StringPtr("module_current")
+ props.Visibility = []string{"//frameworks/base"}
+ ctx.CreateModule(java.LibraryFactory, &props)
+}
+
func createMergedFrameworkModuleLibStubs(ctx android.LoadHookContext, modules []string) {
// The user of this module compiles against the "core" SDK and against non-updatable modules,
// so remove to avoid dupes.
@@ -382,6 +437,27 @@
}
}
+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"}
+
+ ctx.CreateModule(java.LibraryFactory, &props)
+ }
+}
+
func (a *CombinedApis) createInternalModules(ctx android.LoadHookContext) {
bootclasspath := a.properties.Bootclasspath
system_server_classpath := a.properties.System_server_classpath
@@ -397,6 +473,11 @@
createMergedFrameworkModuleLibStubs(ctx, bootclasspath)
createMergedFrameworkImpl(ctx, bootclasspath)
+ createMergedPublicExportableStubs(ctx, bootclasspath)
+ createMergedSystemExportableStubs(ctx, bootclasspath)
+ createMergedTestExportableStubsForNonUpdatableModules(ctx)
+ createMergedFrameworkModuleLibExportableStubs(ctx, bootclasspath)
+
createMergedAnnotationsFilegroups(ctx, bootclasspath, system_server_classpath)
createPublicStubsSourceFilegroup(ctx, bootclasspath)
@@ -404,6 +485,8 @@
createApiContributionDefaults(ctx, bootclasspath)
createFullApiLibraries(ctx)
+
+ createFullExportableApiLibraries(ctx)
}
func combinedApisModuleFactory() android.Module {
diff --git a/core/java/android/hardware/biometrics/IBiometricContextListener.aidl b/core/java/android/hardware/biometrics/IBiometricContextListener.aidl
index 6ac6581..2f58f51 100644
--- a/core/java/android/hardware/biometrics/IBiometricContextListener.aidl
+++ b/core/java/android/hardware/biometrics/IBiometricContextListener.aidl
@@ -38,4 +38,8 @@
// Called when the display state of the device changes.
// Where `displayState` is defined in AuthenticateOptions.DisplayState
void onDisplayStateChanged(int displayState);
+
+ // Called when the HAL ignoring touches state changes.
+ // When true, the HAL ignores touches on the sensor.
+ void onHardwareIgnoreTouchesChanged(boolean shouldIgnore);
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index f974ef4..7d84bb3 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -1931,9 +1931,7 @@
* A matching Activity will only be found if
* {@link NotificationManager#areAutomaticZenRulesUserManaged()} is true.
* <p>
- * Input: Intent's data URI set with an application name, using the "package" schema (like
- * "package:com.my.app").
- * Input: The id of the rule, provided in {@link #EXTRA_AUTOMATIC_ZEN_RULE_ID}.
+ * Input: The id of the rule, provided in the {@link #EXTRA_AUTOMATIC_ZEN_RULE_ID} extra.
* <p>
* Output: Nothing.
*/
diff --git a/core/tests/coretests/src/com/android/internal/os/StoragedUidIoStatsReaderTest.java b/core/tests/coretests/src/com/android/internal/os/StoragedUidIoStatsReaderTest.java
index 84c93c2..80061a5 100644
--- a/core/tests/coretests/src/com/android/internal/os/StoragedUidIoStatsReaderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/StoragedUidIoStatsReaderTest.java
@@ -18,7 +18,6 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.verifyZeroInteractions;
import android.os.FileUtils;
@@ -73,8 +72,7 @@
@Test
public void testReadNonexistentFile() throws Exception {
mStoragedUidIoStatsReader.readAbsolute(mCallback);
- verifyZeroInteractions(mCallback);
-
+ verifyNoMoreInteractions(mCallback);
}
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
index 41f8204..aabc1cf 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
@@ -421,9 +421,9 @@
}
moveTaskToFront(mTaskOrganizer.getRunningTaskInfo(mTaskId));
- if (!mHasLongClicked) {
+ if (!mHasLongClicked && id != R.id.maximize_window) {
final DesktopModeWindowDecoration decoration = mWindowDecorByTaskId.get(mTaskId);
- decoration.closeMaximizeMenu();
+ decoration.closeMaximizeMenuIfNeeded(e);
}
final long eventDuration = e.getEventTime() - e.getDownTime();
@@ -643,7 +643,7 @@
handleCaptionThroughStatusBar(ev, relevantDecor);
}
}
- handleEventOutsideFocusedCaption(ev, relevantDecor);
+ handleEventOutsideCaption(ev, relevantDecor);
// Prevent status bar from reacting to a caption drag.
if (DesktopModeStatus.isEnabled()) {
if (mTransitionDragActive) {
@@ -652,11 +652,17 @@
}
}
- // If an UP/CANCEL action is received outside of caption bounds, turn off handle menu
- private void handleEventOutsideFocusedCaption(MotionEvent ev,
+ /**
+ * If an UP/CANCEL action is received outside of the caption bounds, close the handle and
+ * maximize the menu.
+ *
+ * @param relevantDecor the window decoration of the focused task's caption. This method only
+ * handles motion events outside this caption's bounds.
+ */
+ private void handleEventOutsideCaption(MotionEvent ev,
DesktopModeWindowDecoration relevantDecor) {
// Returns if event occurs within caption
- if (relevantDecor == null || relevantDecor.checkTouchEventInCaptionHandle(ev)) {
+ if (relevantDecor == null || relevantDecor.checkTouchEventInCaption(ev)) {
return;
}
@@ -692,7 +698,7 @@
}
if (dragFromStatusBarAllowed
- && relevantDecor.checkTouchEventInCaptionHandle(ev)) {
+ && relevantDecor.checkTouchEventInFocusedCaptionHandle(ev)) {
mTransitionDragActive = true;
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
index 5f77192..53f806c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
@@ -612,8 +612,7 @@
void closeMaximizeMenuIfNeeded(MotionEvent ev) {
if (!isMaximizeMenuActive()) return;
- final PointF inputPoint = offsetCaptionLocation(ev);
- if (!mMaximizeMenu.isValidMenuInput(inputPoint)) {
+ if (!mMaximizeMenu.isValidMenuInput(ev)) {
closeMaximizeMenu();
}
}
@@ -639,20 +638,34 @@
}
/**
- * Checks if motion event occurs in the caption handle area. This should be used in cases where
+ * Checks if motion event occurs in the caption handle area of a focused caption (the caption on
+ * a task in fullscreen or in multi-windowing mode). This should be used in cases where
* onTouchListener will not work (i.e. when caption is in status bar area).
*
* @param ev the {@link MotionEvent} to check
- * @return {@code true} if event is inside the specified view, {@code false} if not
+ * @return {@code true} if event is inside caption handle view, {@code false} if not
*/
- boolean checkTouchEventInCaptionHandle(MotionEvent ev) {
+ boolean checkTouchEventInFocusedCaptionHandle(MotionEvent ev) {
if (isHandleMenuActive() || !(mWindowDecorViewHolder
instanceof DesktopModeFocusedWindowDecorationViewHolder)) {
return false;
}
+
+ return checkTouchEventInCaption(ev);
+ }
+
+ /**
+ * Checks if touch event occurs in caption.
+ *
+ * @param ev the {@link MotionEvent} to check
+ * @return {@code true} if event is inside caption view, {@code false} if not
+ */
+ boolean checkTouchEventInCaption(MotionEvent ev) {
final PointF inputPoint = offsetCaptionLocation(ev);
- return ((DesktopModeFocusedWindowDecorationViewHolder) mWindowDecorViewHolder)
- .pointInCaption(inputPoint, mResult.mCaptionX);
+ return inputPoint.x >= mResult.mCaptionX
+ && inputPoint.x <= mResult.mCaptionX + mResult.mCaptionWidth
+ && inputPoint.y >= 0
+ && inputPoint.y <= mResult.mCaptionHeight;
}
/**
@@ -668,7 +681,7 @@
// Click if point in caption handle view
final View caption = mResult.mRootView.findViewById(R.id.desktop_mode_caption);
final View handle = caption.findViewById(R.id.caption_handle);
- if (checkTouchEventInCaptionHandle(ev)) {
+ if (checkTouchEventInFocusedCaptionHandle(ev)) {
mOnCaptionButtonClickListener.onClick(handle);
}
} else {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeMenu.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeMenu.kt
index 921708f..794b357 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeMenu.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeMenu.kt
@@ -22,10 +22,10 @@
import android.graphics.PixelFormat
import android.graphics.PointF
import android.view.LayoutInflater
+import android.view.MotionEvent
import android.view.SurfaceControl
import android.view.SurfaceControl.Transaction
import android.view.SurfaceControlViewHost
-import android.view.View
import android.view.View.OnClickListener
import android.view.WindowManager
import android.view.WindowlessWindowManager
@@ -62,6 +62,8 @@
private val cornerRadius = loadDimensionPixelSize(
R.dimen.desktop_mode_maximize_menu_corner_radius
).toFloat()
+ private val menuWidth = loadDimensionPixelSize(R.dimen.desktop_mode_maximize_menu_width)
+ private val menuHeight = loadDimensionPixelSize(R.dimen.desktop_mode_maximize_menu_height)
/** Position the menu relative to the caption's position. */
fun positionMenu(position: PointF, t: Transaction) {
@@ -95,8 +97,6 @@
.setName("Maximize Menu")
.setContainerLayer()
.build()
- val menuWidth = loadDimensionPixelSize(R.dimen.desktop_mode_maximize_menu_width)
- val menuHeight = loadDimensionPixelSize(R.dimen.desktop_mode_maximize_menu_height)
val lp = WindowManager.LayoutParams(
menuWidth,
menuHeight,
@@ -160,14 +160,11 @@
*
* @param inputPoint the input to compare against.
*/
- fun isValidMenuInput(inputPoint: PointF): Boolean {
- val menuView = maximizeMenu?.mWindowViewHost?.view ?: return true
- return !viewsLaidOut() || pointInView(menuView, inputPoint.x - menuPosition.x,
- inputPoint.y - menuPosition.y)
- }
-
- private fun pointInView(v: View, x: Float, y: Float): Boolean {
- return v.left <= x && v.right >= x && v.top <= y && v.bottom >= y
+ fun isValidMenuInput(ev: MotionEvent): Boolean {
+ val x = ev.rawX
+ val y = ev.rawY
+ return !viewsLaidOut() || (menuPosition.x <= x && menuPosition.x + menuWidth >= x &&
+ menuPosition.y <= y && menuPosition.y + menuHeight >= y)
}
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
index 6a9258c..afe837e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
@@ -279,11 +279,12 @@
}
outResult.mCaptionHeight = loadDimensionPixelSize(resources, params.mCaptionHeightId);
- final int captionWidth = params.mCaptionWidthId != Resources.ID_NULL
+ outResult.mCaptionWidth = params.mCaptionWidthId != Resources.ID_NULL
? loadDimensionPixelSize(resources, params.mCaptionWidthId) : taskBounds.width();
- outResult.mCaptionX = (outResult.mWidth - captionWidth) / 2;
+ outResult.mCaptionX = (outResult.mWidth - outResult.mCaptionWidth) / 2;
- startT.setWindowCrop(mCaptionContainerSurface, captionWidth, outResult.mCaptionHeight)
+ startT.setWindowCrop(mCaptionContainerSurface, outResult.mCaptionWidth,
+ outResult.mCaptionHeight)
.setPosition(mCaptionContainerSurface, outResult.mCaptionX, 0 /* y */)
.setLayer(mCaptionContainerSurface, CAPTION_LAYER_Z_ORDER)
.show(mCaptionContainerSurface);
@@ -356,7 +357,7 @@
// Caption view
mCaptionWindowManager.setConfiguration(taskConfig);
final WindowManager.LayoutParams lp =
- new WindowManager.LayoutParams(captionWidth, outResult.mCaptionHeight,
+ new WindowManager.LayoutParams(outResult.mCaptionWidth, outResult.mCaptionHeight,
WindowManager.LayoutParams.TYPE_APPLICATION,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, PixelFormat.TRANSPARENT);
lp.setTitle("Caption of Task=" + mTaskInfo.taskId);
@@ -578,6 +579,7 @@
static class RelayoutResult<T extends View & TaskFocusStateConsumer> {
int mCaptionHeight;
+ int mCaptionWidth;
int mCaptionX;
int mWidth;
int mHeight;
@@ -587,6 +589,7 @@
mWidth = 0;
mHeight = 0;
mCaptionHeight = 0;
+ mCaptionWidth = 0;
mCaptionX = 0;
mRootView = null;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeFocusedWindowDecorationViewHolder.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeFocusedWindowDecorationViewHolder.kt
index 5f77022..6dcae27 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeFocusedWindowDecorationViewHolder.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeFocusedWindowDecorationViewHolder.kt
@@ -5,7 +5,6 @@
import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
import android.content.res.ColorStateList
import android.graphics.Color
-import android.graphics.PointF
import android.view.View
import android.view.WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS
import android.widget.ImageButton
@@ -47,17 +46,6 @@
animateCaptionHandleAlpha(startValue = 0f, endValue = 1f)
}
- /**
- * Returns true if input point is in the caption's view.
- * @param inputPoint the input point relative to the task in full "focus" (i.e. fullscreen).
- */
- fun pointInCaption(inputPoint: PointF, captionX: Int): Boolean {
- return inputPoint.x >= captionX &&
- inputPoint.x <= captionX + captionView.width &&
- inputPoint.y >= 0 &&
- inputPoint.y <= captionView.height
- }
-
private fun getCaptionHandleBarColor(taskInfo: RunningTaskInfo): Int {
return if (shouldUseLightCaptionColors(taskInfo)) {
context.getColor(R.color.desktop_mode_caption_handle_bar_light)
diff --git a/packages/CredentialManager/Android.bp b/packages/CredentialManager/Android.bp
index 991fe41..c292b502 100644
--- a/packages/CredentialManager/Android.bp
+++ b/packages/CredentialManager/Android.bp
@@ -7,19 +7,14 @@
default_applicable_licenses: ["frameworks_base_license"],
}
-android_app {
- name: "CredentialManager",
- defaults: ["platform_app_defaults"],
- certificate: "platform",
+android_library {
+ name: "CredentialManager-handheld",
+
+ platform_apis: true,
+
srcs: ["src/**/*.kt"],
resource_dirs: ["res"],
- dex_preopt: {
- profile_guided: true,
- //TODO: b/312357299 - Update baseline profile
- profile: "profile.txt.prof",
- },
-
static_libs: [
"CredentialManagerShared",
"PlatformComposeCore",
@@ -42,6 +37,23 @@
"androidx.recyclerview_recyclerview",
"kotlinx-coroutines-core",
],
+}
+
+android_app {
+ name: "CredentialManager",
+ defaults: ["platform_app_defaults"],
+ certificate: "platform",
+
+ dex_preopt: {
+ profile_guided: true,
+ //TODO: b/312357299 - Update baseline profile
+ profile: "profile.txt.prof",
+ },
+
+ // Do not add new dependencies here. Add to CredentialManager-handheld instead.
+ static_libs: [
+ "CredentialManager-handheld",
+ ],
platform_apis: true,
privileged: true,
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorActivity.kt b/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorActivity.kt
index c409ba6..f8ffc9e 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorActivity.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorActivity.kt
@@ -34,6 +34,7 @@
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.ui.res.stringResource
import androidx.lifecycle.viewmodel.compose.viewModel
+import com.android.compose.theme.PlatformTheme
import com.android.credentialmanager.common.Constants
import com.android.credentialmanager.common.DialogState
import com.android.credentialmanager.common.ProviderActivityResult
@@ -43,7 +44,6 @@
import com.android.credentialmanager.createflow.hasContentToDisplay
import com.android.credentialmanager.getflow.GetCredentialScreen
import com.android.credentialmanager.getflow.hasContentToDisplay
-import com.android.credentialmanager.ui.theme.PlatformTheme
@ExperimentalMaterialApi
class CredentialSelectorActivity : ComponentActivity() {
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/BottomSheet.kt b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/BottomSheet.kt
index db69b8b..d319e4c 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/BottomSheet.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/BottomSheet.kt
@@ -24,11 +24,11 @@
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import com.android.compose.rememberSystemUiController
+import com.android.compose.theme.LocalAndroidColorScheme
import com.android.credentialmanager.common.material.ModalBottomSheetLayout
import com.android.credentialmanager.common.material.ModalBottomSheetValue
import com.android.credentialmanager.common.material.rememberModalBottomSheetState
import com.android.credentialmanager.ui.theme.EntryShape
-import com.android.credentialmanager.ui.theme.LocalAndroidColorScheme
import kotlinx.coroutines.launch
@@ -54,7 +54,7 @@
setBottomSheetSystemBarsColor(sysUiController)
}
ModalBottomSheetLayout(
- sheetBackgroundColor = LocalAndroidColorScheme.current.colorSurfaceBright,
+ sheetBackgroundColor = LocalAndroidColorScheme.current.surfaceBright,
modifier = Modifier.background(Color.Transparent),
sheetState = state,
sheetContent = sheetContent,
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/Cards.kt b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/Cards.kt
index 3976f9a..bdfe399 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/Cards.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/Cards.kt
@@ -30,8 +30,8 @@
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
+import com.android.compose.theme.LocalAndroidColorScheme
import com.android.credentialmanager.ui.theme.Shapes
-import com.android.credentialmanager.ui.theme.LocalAndroidColorScheme
/**
* Container card for the whole sheet.
@@ -50,7 +50,7 @@
modifier = modifier.fillMaxWidth().wrapContentHeight(),
border = null,
colors = CardDefaults.cardColors(
- containerColor = LocalAndroidColorScheme.current.colorSurfaceBright,
+ containerColor = LocalAndroidColorScheme.current.surfaceBright,
),
) {
if (topAppBar != null) {
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/Entry.kt b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/Entry.kt
index 1c394ec..a6253b8 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/Entry.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/Entry.kt
@@ -56,9 +56,9 @@
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.dp
+import com.android.compose.theme.LocalAndroidColorScheme
import com.android.credentialmanager.R
import com.android.credentialmanager.ui.theme.EntryShape
-import com.android.credentialmanager.ui.theme.LocalAndroidColorScheme
import com.android.credentialmanager.ui.theme.Shapes
@Composable
@@ -168,7 +168,7 @@
// Decorative purpose only.
contentDescription = null,
modifier = Modifier.size(24.dp),
- tint = LocalAndroidColorScheme.current.colorOnSurfaceVariant,
+ tint = LocalAndroidColorScheme.current.onSurfaceVariant,
)
}
}
@@ -182,7 +182,7 @@
Icon(
modifier = iconSize,
bitmap = iconImageBitmap,
- tint = LocalAndroidColorScheme.current.colorOnSurfaceVariant,
+ tint = LocalAndroidColorScheme.current.onSurfaceVariant,
// Decorative purpose only.
contentDescription = null,
)
@@ -206,7 +206,7 @@
Icon(
modifier = iconSize,
imageVector = iconImageVector,
- tint = LocalAndroidColorScheme.current.colorOnSurfaceVariant,
+ tint = LocalAndroidColorScheme.current.onSurfaceVariant,
// Decorative purpose only.
contentDescription = null,
)
@@ -218,7 +218,7 @@
Icon(
modifier = iconSize,
painter = iconPainter,
- tint = LocalAndroidColorScheme.current.colorOnSurfaceVariant,
+ tint = LocalAndroidColorScheme.current.onSurfaceVariant,
// Decorative purpose only.
contentDescription = null,
)
@@ -229,9 +229,9 @@
},
border = null,
colors = SuggestionChipDefaults.suggestionChipColors(
- containerColor = LocalAndroidColorScheme.current.colorSurfaceContainerHigh,
- labelColor = LocalAndroidColorScheme.current.colorOnSurfaceVariant,
- iconContentColor = LocalAndroidColorScheme.current.colorOnSurfaceVariant,
+ containerColor = LocalAndroidColorScheme.current.surfaceContainerHigh,
+ labelColor = LocalAndroidColorScheme.current.onSurfaceVariant,
+ iconContentColor = LocalAndroidColorScheme.current.onSurfaceVariant,
),
)
}
@@ -294,7 +294,7 @@
Icon(
modifier = Modifier.size(24.dp),
painter = leadingIconPainter,
- tint = LocalAndroidColorScheme.current.colorOnSurfaceVariant,
+ tint = LocalAndroidColorScheme.current.onSurfaceVariant,
// Decorative purpose only.
contentDescription = null,
)
@@ -353,7 +353,7 @@
R.string.accessibility_back_arrow_button
),
modifier = Modifier.size(24.dp).autoMirrored(),
- tint = LocalAndroidColorScheme.current.colorOnSurfaceVariant,
+ tint = LocalAndroidColorScheme.current.onSurfaceVariant,
)
}
}
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/SectionHeader.kt b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/SectionHeader.kt
index 2df0c7a9..342af3b 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/SectionHeader.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/SectionHeader.kt
@@ -24,20 +24,20 @@
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
-import com.android.credentialmanager.ui.theme.LocalAndroidColorScheme
+import com.android.compose.theme.LocalAndroidColorScheme
@Composable
fun CredentialListSectionHeader(text: String, isFirstSection: Boolean) {
InternalSectionHeader(
text = text,
- color = LocalAndroidColorScheme.current.colorOnSurfaceVariant,
+ color = LocalAndroidColorScheme.current.onSurfaceVariant,
applyTopPadding = !isFirstSection
)
}
@Composable
fun MoreAboutPasskeySectionHeader(text: String) {
- InternalSectionHeader(text, LocalAndroidColorScheme.current.colorOnSurface)
+ InternalSectionHeader(text, LocalAndroidColorScheme.current.onSurface)
}
@Composable
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/SystemUiControllerUtils.kt b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/SystemUiControllerUtils.kt
index a619523..b4075f1 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/SystemUiControllerUtils.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/SystemUiControllerUtils.kt
@@ -19,8 +19,8 @@
import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.Color
import com.android.compose.SystemUiController
+import com.android.compose.theme.LocalAndroidColorScheme
import com.android.credentialmanager.common.material.ModalBottomSheetDefaults
-import com.android.credentialmanager.ui.theme.LocalAndroidColorScheme
@Composable
fun setTransparentSystemBarsColor(sysUiController: SystemUiController) {
@@ -34,7 +34,7 @@
darkIcons = false
)
sysUiController.setNavigationBarColor(
- color = LocalAndroidColorScheme.current.colorSurfaceBright,
+ color = LocalAndroidColorScheme.current.surfaceBright,
darkIcons = false
)
}
\ No newline at end of file
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/Texts.kt b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/Texts.kt
index 6b46636..9111e61 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/Texts.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/Texts.kt
@@ -25,7 +25,7 @@
import androidx.compose.ui.text.TextLayoutResult
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
-import com.android.credentialmanager.ui.theme.LocalAndroidColorScheme
+import com.android.compose.theme.LocalAndroidColorScheme
/**
* The headline for a screen. E.g. "Create a passkey for X", "Choose a saved sign-in for X".
@@ -37,7 +37,7 @@
Text(
modifier = modifier.wrapContentSize(),
text = text,
- color = LocalAndroidColorScheme.current.colorOnSurface,
+ color = LocalAndroidColorScheme.current.onSurface,
textAlign = TextAlign.Center,
style = MaterialTheme.typography.headlineSmall,
)
@@ -51,7 +51,7 @@
Text(
modifier = modifier.wrapContentSize(),
text = text,
- color = LocalAndroidColorScheme.current.colorOnSurfaceVariant,
+ color = LocalAndroidColorScheme.current.onSurfaceVariant,
style = MaterialTheme.typography.bodyMedium,
)
}
@@ -69,7 +69,7 @@
Text(
modifier = modifier.wrapContentSize(),
text = text,
- color = LocalAndroidColorScheme.current.colorOnSurfaceVariant,
+ color = LocalAndroidColorScheme.current.onSurfaceVariant,
style = MaterialTheme.typography.bodySmall,
overflow = TextOverflow.Ellipsis,
maxLines = if (enforceOneLine) 1 else Int.MAX_VALUE,
@@ -85,7 +85,7 @@
Text(
modifier = modifier.wrapContentSize(),
text = text,
- color = LocalAndroidColorScheme.current.colorOnSurface,
+ color = LocalAndroidColorScheme.current.onSurface,
style = MaterialTheme.typography.titleLarge,
)
}
@@ -103,7 +103,7 @@
Text(
modifier = modifier.wrapContentSize(),
text = text,
- color = LocalAndroidColorScheme.current.colorOnSurface,
+ color = LocalAndroidColorScheme.current.onSurface,
style = MaterialTheme.typography.titleSmall,
overflow = TextOverflow.Ellipsis,
maxLines = if (enforceOneLine) 1 else Int.MAX_VALUE,
@@ -159,7 +159,7 @@
modifier = modifier.wrapContentSize(),
text = text,
textAlign = TextAlign.Center,
- color = LocalAndroidColorScheme.current.colorOnSurfaceVariant,
+ color = LocalAndroidColorScheme.current.onSurfaceVariant,
style = MaterialTheme.typography.labelLarge,
)
}
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt
index 14a9165..f261d1f 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt
@@ -46,6 +46,7 @@
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.core.graphics.drawable.toBitmap
+import com.android.compose.theme.LocalAndroidColorScheme
import com.android.credentialmanager.CredentialSelectorViewModel
import com.android.credentialmanager.R
import com.android.credentialmanager.model.EntryInfo
@@ -70,7 +71,6 @@
import com.android.credentialmanager.logging.CreateCredentialEvent
import com.android.credentialmanager.model.creation.CreateOptionInfo
import com.android.credentialmanager.model.creation.RemoteInfo
-import com.android.credentialmanager.ui.theme.LocalAndroidColorScheme
import com.android.internal.logging.UiEventLogger.UiEventEnum
@Composable
@@ -460,7 +460,7 @@
item {
Divider(
thickness = 1.dp,
- color = LocalAndroidColorScheme.current.colorOutlineVariant,
+ color = LocalAndroidColorScheme.current.outlineVariant,
modifier = Modifier.padding(vertical = 16.dp)
)
}
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetModel.kt b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetModel.kt
index a291f59..458a99a 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetModel.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetModel.kt
@@ -26,7 +26,7 @@
import com.android.internal.util.Preconditions
data class GetCredentialUiState(
- val isRequestForAllOptions: Boolean,
+ val isRequestForAllOptions: Boolean,
val providerInfoList: List<ProviderInfo>,
val requestDisplayInfo: RequestDisplayInfo,
val providerDisplayInfo: ProviderDisplayInfo = toProviderDisplayInfo(providerInfoList),
@@ -165,7 +165,7 @@
)
}
-private fun toActiveEntry(
+fun toActiveEntry(
providerDisplayInfo: ProviderDisplayInfo,
): EntryInfo? {
val sortedUserNameToCredentialEntryList =
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/ui/theme/AndroidColorScheme.kt b/packages/CredentialManager/src/com/android/credentialmanager/ui/theme/AndroidColorScheme.kt
deleted file mode 100644
index a33904d..0000000
--- a/packages/CredentialManager/src/com/android/credentialmanager/ui/theme/AndroidColorScheme.kt
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.credentialmanager.ui.theme
-
-import android.annotation.ColorInt
-import android.content.Context
-import androidx.compose.runtime.staticCompositionLocalOf
-import androidx.compose.ui.graphics.Color
-import com.android.internal.R
-
-/** File copied from PlatformComposeCore. */
-
-/** CompositionLocal used to pass [AndroidColorScheme] down the tree. */
-val LocalAndroidColorScheme =
- staticCompositionLocalOf<AndroidColorScheme> {
- throw IllegalStateException(
- "No AndroidColorScheme configured. Make sure to use LocalAndroidColorScheme in a " +
- "Composable surrounded by a PlatformTheme {}."
- )
- }
-
-/**
- * The Android color scheme.
- *
- * Important: Use M3 colors from MaterialTheme.colorScheme whenever possible instead. In the future,
- * most of the colors in this class will be removed in favor of their M3 counterpart.
- */
-class AndroidColorScheme internal constructor(context: Context) {
- val colorSurfaceBright = getColor(context, R.attr.materialColorSurfaceBright)
- val colorSurfaceContainerHigh = getColor(context, R.attr.materialColorSurfaceContainerHigh)
- val colorOutlineVariant = getColor(context, R.attr.materialColorOutlineVariant)
- val colorOnSurface = getColor(context, R.attr.materialColorOnSurface)
- val colorOnSurfaceVariant = getColor(context, R.attr.materialColorOnSurfaceVariant)
-
- companion object {
- fun getColor(context: Context, attr: Int): Color {
- val ta = context.obtainStyledAttributes(intArrayOf(attr))
- @ColorInt val color = ta.getColor(0, 0)
- ta.recycle()
- return Color(color)
- }
- }
-}
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/ui/theme/Color.kt b/packages/CredentialManager/src/com/android/credentialmanager/ui/theme/Color.kt
deleted file mode 100644
index c923845..0000000
--- a/packages/CredentialManager/src/com/android/credentialmanager/ui/theme/Color.kt
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.credentialmanager.ui.theme
-
-import android.annotation.AttrRes
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.ReadOnlyComposable
-import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.platform.LocalContext
-
-/** Read the [Color] from the given [attribute]. */
-@Composable
-@ReadOnlyComposable
-fun colorAttr(@AttrRes attribute: Int): Color {
- return AndroidColorScheme.getColor(LocalContext.current, attribute)
-}
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/ui/theme/PlatformTheme.kt b/packages/CredentialManager/src/com/android/credentialmanager/ui/theme/PlatformTheme.kt
deleted file mode 100644
index 2f1ce68..0000000
--- a/packages/CredentialManager/src/com/android/credentialmanager/ui/theme/PlatformTheme.kt
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.credentialmanager.ui.theme
-
-import androidx.compose.foundation.isSystemInDarkTheme
-import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.dynamicDarkColorScheme
-import androidx.compose.material3.dynamicLightColorScheme
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.CompositionLocalProvider
-import androidx.compose.runtime.remember
-import androidx.compose.ui.platform.LocalContext
-import com.android.credentialmanager.ui.theme.typography.TypeScaleTokens
-import com.android.credentialmanager.ui.theme.typography.TypefaceNames
-import com.android.credentialmanager.ui.theme.typography.TypefaceTokens
-import com.android.credentialmanager.ui.theme.typography.TypographyTokens
-import com.android.credentialmanager.ui.theme.typography.platformTypography
-
-/** File copied from PlatformComposeCore. */
-
-/**
- * The Material 3 theme that should wrap all Platform Composables.
- *
- * TODO(b/280685309): Merge with the official SysUI platform theme.
- */
-@Composable
-fun PlatformTheme(
- isDarkTheme: Boolean = isSystemInDarkTheme(),
- content: @Composable () -> Unit,
-) {
- val context = LocalContext.current
-
- val colorScheme =
- if (isDarkTheme) {
- dynamicDarkColorScheme(context)
- } else {
- dynamicLightColorScheme(context)
- }
- val androidColorScheme = AndroidColorScheme(context)
- val typefaceNames = remember(context) { TypefaceNames.get(context) }
- val typography =
- remember(typefaceNames) {
- platformTypography(TypographyTokens(TypeScaleTokens(TypefaceTokens(typefaceNames))))
- }
-
- MaterialTheme(colorScheme, typography = typography) {
- CompositionLocalProvider(
- LocalAndroidColorScheme provides androidColorScheme,
- ) {
- content()
- }
- }
-}
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/ui/theme/typography/PlatformTypography.kt b/packages/CredentialManager/src/com/android/credentialmanager/ui/theme/typography/PlatformTypography.kt
deleted file mode 100644
index 984e4f1..0000000
--- a/packages/CredentialManager/src/com/android/credentialmanager/ui/theme/typography/PlatformTypography.kt
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.credentialmanager.ui.theme.typography
-
-import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.Typography
-
-/** File copied from PlatformComposeCore. */
-
-/**
- * The typography for Platform Compose code.
- *
- * Do not use directly and call [MaterialTheme.typography] instead to access the different text
- * styles.
- */
-internal fun platformTypography(typographyTokens: TypographyTokens): Typography {
- return Typography(
- displayLarge = typographyTokens.displayLarge,
- displayMedium = typographyTokens.displayMedium,
- displaySmall = typographyTokens.displaySmall,
- headlineLarge = typographyTokens.headlineLarge,
- headlineMedium = typographyTokens.headlineMedium,
- headlineSmall = typographyTokens.headlineSmall,
- titleLarge = typographyTokens.titleLarge,
- titleMedium = typographyTokens.titleMedium,
- titleSmall = typographyTokens.titleSmall,
- bodyLarge = typographyTokens.bodyLarge,
- bodyMedium = typographyTokens.bodyMedium,
- bodySmall = typographyTokens.bodySmall,
- labelLarge = typographyTokens.labelLarge,
- labelMedium = typographyTokens.labelMedium,
- labelSmall = typographyTokens.labelSmall,
- )
-}
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/ui/theme/typography/TypeScaleTokens.kt b/packages/CredentialManager/src/com/android/credentialmanager/ui/theme/typography/TypeScaleTokens.kt
deleted file mode 100644
index b2dd207..0000000
--- a/packages/CredentialManager/src/com/android/credentialmanager/ui/theme/typography/TypeScaleTokens.kt
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.credentialmanager.ui.theme.typography
-
-import androidx.compose.ui.unit.sp
-
-/** File copied from PlatformComposeCore. */
-internal class TypeScaleTokens(typefaceTokens: TypefaceTokens) {
- val bodyLargeFont = typefaceTokens.plain
- val bodyLargeLineHeight = 24.0.sp
- val bodyLargeSize = 16.sp
- val bodyLargeTracking = 0.0.sp
- val bodyLargeWeight = TypefaceTokens.WeightRegular
- val bodyMediumFont = typefaceTokens.plain
- val bodyMediumLineHeight = 20.0.sp
- val bodyMediumSize = 14.sp
- val bodyMediumTracking = 0.0.sp
- val bodyMediumWeight = TypefaceTokens.WeightRegular
- val bodySmallFont = typefaceTokens.plain
- val bodySmallLineHeight = 16.0.sp
- val bodySmallSize = 12.sp
- val bodySmallTracking = 0.1.sp
- val bodySmallWeight = TypefaceTokens.WeightRegular
- val displayLargeFont = typefaceTokens.brand
- val displayLargeLineHeight = 64.0.sp
- val displayLargeSize = 57.sp
- val displayLargeTracking = 0.0.sp
- val displayLargeWeight = TypefaceTokens.WeightRegular
- val displayMediumFont = typefaceTokens.brand
- val displayMediumLineHeight = 52.0.sp
- val displayMediumSize = 45.sp
- val displayMediumTracking = 0.0.sp
- val displayMediumWeight = TypefaceTokens.WeightRegular
- val displaySmallFont = typefaceTokens.brand
- val displaySmallLineHeight = 44.0.sp
- val displaySmallSize = 36.sp
- val displaySmallTracking = 0.0.sp
- val displaySmallWeight = TypefaceTokens.WeightRegular
- val headlineLargeFont = typefaceTokens.brand
- val headlineLargeLineHeight = 40.0.sp
- val headlineLargeSize = 32.sp
- val headlineLargeTracking = 0.0.sp
- val headlineLargeWeight = TypefaceTokens.WeightRegular
- val headlineMediumFont = typefaceTokens.brand
- val headlineMediumLineHeight = 36.0.sp
- val headlineMediumSize = 28.sp
- val headlineMediumTracking = 0.0.sp
- val headlineMediumWeight = TypefaceTokens.WeightRegular
- val headlineSmallFont = typefaceTokens.brand
- val headlineSmallLineHeight = 32.0.sp
- val headlineSmallSize = 24.sp
- val headlineSmallTracking = 0.0.sp
- val headlineSmallWeight = TypefaceTokens.WeightRegular
- val labelLargeFont = typefaceTokens.plain
- val labelLargeLineHeight = 20.0.sp
- val labelLargeSize = 14.sp
- val labelLargeTracking = 0.0.sp
- val labelLargeWeight = TypefaceTokens.WeightMedium
- val labelMediumFont = typefaceTokens.plain
- val labelMediumLineHeight = 16.0.sp
- val labelMediumSize = 12.sp
- val labelMediumTracking = 0.1.sp
- val labelMediumWeight = TypefaceTokens.WeightMedium
- val labelSmallFont = typefaceTokens.plain
- val labelSmallLineHeight = 16.0.sp
- val labelSmallSize = 11.sp
- val labelSmallTracking = 0.1.sp
- val labelSmallWeight = TypefaceTokens.WeightMedium
- val titleLargeFont = typefaceTokens.brand
- val titleLargeLineHeight = 28.0.sp
- val titleLargeSize = 22.sp
- val titleLargeTracking = 0.0.sp
- val titleLargeWeight = TypefaceTokens.WeightRegular
- val titleMediumFont = typefaceTokens.plain
- val titleMediumLineHeight = 24.0.sp
- val titleMediumSize = 16.sp
- val titleMediumTracking = 0.0.sp
- val titleMediumWeight = TypefaceTokens.WeightMedium
- val titleSmallFont = typefaceTokens.plain
- val titleSmallLineHeight = 20.0.sp
- val titleSmallSize = 14.sp
- val titleSmallTracking = 0.0.sp
- val titleSmallWeight = TypefaceTokens.WeightMedium
-}
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/ui/theme/typography/TypefaceTokens.kt b/packages/CredentialManager/src/com/android/credentialmanager/ui/theme/typography/TypefaceTokens.kt
deleted file mode 100644
index 3cc761f..0000000
--- a/packages/CredentialManager/src/com/android/credentialmanager/ui/theme/typography/TypefaceTokens.kt
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-@file:OptIn(ExperimentalTextApi::class)
-
-package com.android.credentialmanager.ui.theme.typography
-
-import android.content.Context
-import androidx.compose.ui.text.ExperimentalTextApi
-import androidx.compose.ui.text.font.DeviceFontFamilyName
-import androidx.compose.ui.text.font.Font
-import androidx.compose.ui.text.font.FontFamily
-import androidx.compose.ui.text.font.FontWeight
-
-/** File copied from PlatformComposeCore. */
-internal class TypefaceTokens(typefaceNames: TypefaceNames) {
- companion object {
- val WeightMedium = FontWeight.Medium
- val WeightRegular = FontWeight.Normal
- }
-
- private val brandFont = DeviceFontFamilyName(typefaceNames.brand)
- private val plainFont = DeviceFontFamilyName(typefaceNames.plain)
-
- val brand =
- FontFamily(
- Font(brandFont, weight = WeightMedium),
- Font(brandFont, weight = WeightRegular),
- )
- val plain =
- FontFamily(
- Font(plainFont, weight = WeightMedium),
- Font(plainFont, weight = WeightRegular),
- )
-}
-
-internal data class TypefaceNames
-private constructor(
- val brand: String,
- val plain: String,
-) {
- private enum class Config(val configName: String, val default: String) {
- Brand("config_headlineFontFamily", "sans-serif"),
- Plain("config_bodyFontFamily", "sans-serif"),
- }
-
- companion object {
- fun get(context: Context): TypefaceNames {
- return TypefaceNames(
- brand = getTypefaceName(context, Config.Brand),
- plain = getTypefaceName(context, Config.Plain),
- )
- }
-
- private fun getTypefaceName(context: Context, config: Config): String {
- return context
- .getString(context.resources.getIdentifier(config.configName, "string", "android"))
- .takeIf { it.isNotEmpty() }
- ?: config.default
- }
- }
-}
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/ui/theme/typography/TypographyTokens.kt b/packages/CredentialManager/src/com/android/credentialmanager/ui/theme/typography/TypographyTokens.kt
deleted file mode 100644
index aadab92..0000000
--- a/packages/CredentialManager/src/com/android/credentialmanager/ui/theme/typography/TypographyTokens.kt
+++ /dev/null
@@ -1,143 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.credentialmanager.ui.theme.typography
-
-import androidx.compose.ui.text.TextStyle
-
-/** File copied from PlatformComposeCore. */
-internal class TypographyTokens(typeScaleTokens: TypeScaleTokens) {
- val bodyLarge =
- TextStyle(
- fontFamily = typeScaleTokens.bodyLargeFont,
- fontWeight = typeScaleTokens.bodyLargeWeight,
- fontSize = typeScaleTokens.bodyLargeSize,
- lineHeight = typeScaleTokens.bodyLargeLineHeight,
- letterSpacing = typeScaleTokens.bodyLargeTracking,
- )
- val bodyMedium =
- TextStyle(
- fontFamily = typeScaleTokens.bodyMediumFont,
- fontWeight = typeScaleTokens.bodyMediumWeight,
- fontSize = typeScaleTokens.bodyMediumSize,
- lineHeight = typeScaleTokens.bodyMediumLineHeight,
- letterSpacing = typeScaleTokens.bodyMediumTracking,
- )
- val bodySmall =
- TextStyle(
- fontFamily = typeScaleTokens.bodySmallFont,
- fontWeight = typeScaleTokens.bodySmallWeight,
- fontSize = typeScaleTokens.bodySmallSize,
- lineHeight = typeScaleTokens.bodySmallLineHeight,
- letterSpacing = typeScaleTokens.bodySmallTracking,
- )
- val displayLarge =
- TextStyle(
- fontFamily = typeScaleTokens.displayLargeFont,
- fontWeight = typeScaleTokens.displayLargeWeight,
- fontSize = typeScaleTokens.displayLargeSize,
- lineHeight = typeScaleTokens.displayLargeLineHeight,
- letterSpacing = typeScaleTokens.displayLargeTracking,
- )
- val displayMedium =
- TextStyle(
- fontFamily = typeScaleTokens.displayMediumFont,
- fontWeight = typeScaleTokens.displayMediumWeight,
- fontSize = typeScaleTokens.displayMediumSize,
- lineHeight = typeScaleTokens.displayMediumLineHeight,
- letterSpacing = typeScaleTokens.displayMediumTracking,
- )
- val displaySmall =
- TextStyle(
- fontFamily = typeScaleTokens.displaySmallFont,
- fontWeight = typeScaleTokens.displaySmallWeight,
- fontSize = typeScaleTokens.displaySmallSize,
- lineHeight = typeScaleTokens.displaySmallLineHeight,
- letterSpacing = typeScaleTokens.displaySmallTracking,
- )
- val headlineLarge =
- TextStyle(
- fontFamily = typeScaleTokens.headlineLargeFont,
- fontWeight = typeScaleTokens.headlineLargeWeight,
- fontSize = typeScaleTokens.headlineLargeSize,
- lineHeight = typeScaleTokens.headlineLargeLineHeight,
- letterSpacing = typeScaleTokens.headlineLargeTracking,
- )
- val headlineMedium =
- TextStyle(
- fontFamily = typeScaleTokens.headlineMediumFont,
- fontWeight = typeScaleTokens.headlineMediumWeight,
- fontSize = typeScaleTokens.headlineMediumSize,
- lineHeight = typeScaleTokens.headlineMediumLineHeight,
- letterSpacing = typeScaleTokens.headlineMediumTracking,
- )
- val headlineSmall =
- TextStyle(
- fontFamily = typeScaleTokens.headlineSmallFont,
- fontWeight = typeScaleTokens.headlineSmallWeight,
- fontSize = typeScaleTokens.headlineSmallSize,
- lineHeight = typeScaleTokens.headlineSmallLineHeight,
- letterSpacing = typeScaleTokens.headlineSmallTracking,
- )
- val labelLarge =
- TextStyle(
- fontFamily = typeScaleTokens.labelLargeFont,
- fontWeight = typeScaleTokens.labelLargeWeight,
- fontSize = typeScaleTokens.labelLargeSize,
- lineHeight = typeScaleTokens.labelLargeLineHeight,
- letterSpacing = typeScaleTokens.labelLargeTracking,
- )
- val labelMedium =
- TextStyle(
- fontFamily = typeScaleTokens.labelMediumFont,
- fontWeight = typeScaleTokens.labelMediumWeight,
- fontSize = typeScaleTokens.labelMediumSize,
- lineHeight = typeScaleTokens.labelMediumLineHeight,
- letterSpacing = typeScaleTokens.labelMediumTracking,
- )
- val labelSmall =
- TextStyle(
- fontFamily = typeScaleTokens.labelSmallFont,
- fontWeight = typeScaleTokens.labelSmallWeight,
- fontSize = typeScaleTokens.labelSmallSize,
- lineHeight = typeScaleTokens.labelSmallLineHeight,
- letterSpacing = typeScaleTokens.labelSmallTracking,
- )
- val titleLarge =
- TextStyle(
- fontFamily = typeScaleTokens.titleLargeFont,
- fontWeight = typeScaleTokens.titleLargeWeight,
- fontSize = typeScaleTokens.titleLargeSize,
- lineHeight = typeScaleTokens.titleLargeLineHeight,
- letterSpacing = typeScaleTokens.titleLargeTracking,
- )
- val titleMedium =
- TextStyle(
- fontFamily = typeScaleTokens.titleMediumFont,
- fontWeight = typeScaleTokens.titleMediumWeight,
- fontSize = typeScaleTokens.titleMediumSize,
- lineHeight = typeScaleTokens.titleMediumLineHeight,
- letterSpacing = typeScaleTokens.titleMediumTracking,
- )
- val titleSmall =
- TextStyle(
- fontFamily = typeScaleTokens.titleSmallFont,
- fontWeight = typeScaleTokens.titleSmallWeight,
- fontSize = typeScaleTokens.titleSmallSize,
- lineHeight = typeScaleTokens.titleSmallLineHeight,
- letterSpacing = typeScaleTokens.titleSmallTracking,
- )
-}
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/Android.bp b/packages/SystemUI/accessibility/accessibilitymenu/Android.bp
index 6c75b434..0df9bac 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/Android.bp
+++ b/packages/SystemUI/accessibility/accessibilitymenu/Android.bp
@@ -37,6 +37,7 @@
"androidx.preference_preference",
"androidx.viewpager_viewpager",
"SettingsLibDisplayUtils",
+ "SettingsLibSettingsTheme",
"com_android_a11y_menu_flags_lib",
],
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/AndroidManifest.xml b/packages/SystemUI/accessibility/accessibilitymenu/AndroidManifest.xml
index ca84265..648cc3b 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/AndroidManifest.xml
+++ b/packages/SystemUI/accessibility/accessibilitymenu/AndroidManifest.xml
@@ -40,7 +40,7 @@
android:exported="true"
android:label="@string/accessibility_menu_settings_name"
android:launchMode="singleTop"
- android:theme="@style/AccessibilityMenuSettings">
+ android:theme="@style/Theme.SettingsBase">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/aconfig/accessibility.aconfig b/packages/SystemUI/accessibility/accessibilitymenu/aconfig/accessibility.aconfig
index eadcd7c..f5db6a4 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/aconfig/accessibility.aconfig
+++ b/packages/SystemUI/accessibility/accessibilitymenu/aconfig/accessibility.aconfig
@@ -8,10 +8,3 @@
description: "Hides the AccessibilityMenuService UI before taking action instead of after."
bug: "292020123"
}
-
-flag {
- name: "a11y_menu_settings_back_button_fix_and_large_button_sizing"
- namespace: "accessibility"
- description: "Provides/restores back button functionality for the a11yMenu settings page. Also, fixes sizing problems with large shortcut buttons."
- bug: "298467628"
-}
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/values-night/styles.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/values-night/styles.xml
index 1f57654..81b3152 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/res/values-night/styles.xml
+++ b/packages/SystemUI/accessibility/accessibilitymenu/res/values-night/styles.xml
@@ -16,10 +16,6 @@
-->
<resources>
- <style name="AccessibilityMenuSettings" parent="android:Theme.DeviceDefault.DayNight">
- <item name="android:windowLightStatusBar">false</item>
- </style>
-
<!--Adds the theme to support SnackBar component and user configurable theme. -->
<style name="ServiceTheme" parent="android:Theme.DeviceDefault.DayNight">
<item name="android:colorControlNormal">@color/colorControlNormal</item>
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/values/styles.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/values/styles.xml
index a2508cd..4169155 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/res/values/styles.xml
+++ b/packages/SystemUI/accessibility/accessibilitymenu/res/values/styles.xml
@@ -16,11 +16,6 @@
-->
<resources>
- <!--The theme is for preference CollapsingToolbarBaseActivity settings-->
- <style name="AccessibilityMenuSettings" parent="android:Theme.DeviceDefault.DayNight">
- <item name="android:windowLightStatusBar">true</item>
- </style>
-
<!--Adds the theme to support SnackBar component and user configurable theme. -->
<style name="ServiceTheme" parent="android:Theme.DeviceDefault.Light">
<item name="android:colorControlNormal">@color/colorControlNormal</item>
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/activity/A11yMenuSettingsActivity.java b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/activity/A11yMenuSettingsActivity.java
index bf51e23..ab8f97a 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/activity/A11yMenuSettingsActivity.java
+++ b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/activity/A11yMenuSettingsActivity.java
@@ -35,7 +35,6 @@
import androidx.preference.PreferenceFragmentCompat;
import androidx.preference.PreferenceManager;
-import com.android.systemui.accessibility.accessibilitymenu.Flags;
import com.android.systemui.accessibility.accessibilitymenu.R;
/**
@@ -56,28 +55,17 @@
ActionBar actionBar = getActionBar();
actionBar.setDisplayShowCustomEnabled(true);
-
- if (Flags.a11yMenuSettingsBackButtonFixAndLargeButtonSizing()) {
- actionBar.setDisplayHomeAsUpEnabled(true);
- }
+ actionBar.setDisplayHomeAsUpEnabled(true);
actionBar.setCustomView(R.layout.preferences_action_bar);
((TextView) findViewById(R.id.action_bar_title)).setText(
getResources().getString(R.string.accessibility_menu_settings_name)
);
- actionBar.setDisplayOptions(
- ActionBar.DISPLAY_TITLE_MULTIPLE_LINES
- | ActionBar.DISPLAY_SHOW_TITLE
- | ActionBar.DISPLAY_HOME_AS_UP);
}
@Override
public boolean onNavigateUp() {
- if (Flags.a11yMenuSettingsBackButtonFixAndLargeButtonSizing()) {
- mCallback.onBackInvoked();
- return true;
- } else {
- return false;
- }
+ mCallback.onBackInvoked();
+ return true;
}
/**
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuAdapter.java b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuAdapter.java
index c4f372c..c2cf6e1 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuAdapter.java
+++ b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuAdapter.java
@@ -28,7 +28,6 @@
import android.widget.TextView;
import com.android.systemui.accessibility.accessibilitymenu.AccessibilityMenuService;
-import com.android.systemui.accessibility.accessibilitymenu.Flags;
import com.android.systemui.accessibility.accessibilitymenu.R;
import com.android.systemui.accessibility.accessibilitymenu.activity.A11yMenuSettingsActivity.A11yMenuPreferenceFragment;
import com.android.systemui.accessibility.accessibilitymenu.model.A11yMenuShortcut;
@@ -81,10 +80,8 @@
if (convertView == null) {
convertView = mInflater.inflate(R.layout.grid_item, parent, false);
- if (Flags.a11yMenuSettingsBackButtonFixAndLargeButtonSizing()) {
- configureShortcutSize(convertView,
- A11yMenuPreferenceFragment.isLargeButtonsEnabled(mService));
- }
+ configureShortcutSize(convertView,
+ A11yMenuPreferenceFragment.isLargeButtonsEnabled(mService));
}
A11yMenuShortcut shortcutItem = (A11yMenuShortcut) getItem(position);
@@ -147,15 +144,6 @@
ImageButton shortcutIconButton = convertView.findViewById(R.id.shortcutIconBtn);
TextView shortcutLabel = convertView.findViewById(R.id.shortcutLabel);
- if (!Flags.a11yMenuSettingsBackButtonFixAndLargeButtonSizing()) {
- if (A11yMenuPreferenceFragment.isLargeButtonsEnabled(mService)) {
- ViewGroup.LayoutParams params = shortcutIconButton.getLayoutParams();
- params.width = (int) (params.width * LARGE_BUTTON_SCALE);
- params.height = (int) (params.height * LARGE_BUTTON_SCALE);
- shortcutLabel.setTextSize(android.util.TypedValue.COMPLEX_UNIT_PX, mLargeTextSize);
- }
- }
-
if (shortcutItem.getId() == A11yMenuShortcut.ShortcutId.UNSPECIFIED_ID_VALUE.ordinal()) {
// Sets empty shortcut icon and label when the shortcut is ADD_ITEM.
shortcutIconButton.setImageResource(android.R.color.transparent);
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
index d76f0ff..91a4d2e 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
@@ -88,8 +88,6 @@
import com.android.systemui.communal.shared.model.CommunalContentSize
import com.android.systemui.communal.ui.viewmodel.BaseCommunalViewModel
import com.android.systemui.communal.ui.viewmodel.CommunalEditModeViewModel
-import com.android.systemui.media.controls.ui.MediaHierarchyManager
-import com.android.systemui.media.controls.ui.MediaHostState
import com.android.systemui.res.R
@Composable
@@ -576,10 +574,6 @@
AndroidView(
modifier = modifier,
factory = {
- viewModel.mediaHost.expansion = MediaHostState.EXPANDED
- viewModel.mediaHost.showsOnlyActiveMedia = false
- viewModel.mediaHost.falsingProtectionNeeded = false
- viewModel.mediaHost.init(MediaHierarchyManager.LOCATION_COMMUNAL_HUB)
viewModel.mediaHost.hostView.layoutParams =
FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
index 02d30c5e..bc3ca1b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
@@ -230,11 +230,7 @@
sceneTransitionStateFlow =
MutableStateFlow(ObservableTransitionState.Idle(SceneKey.Lockscreen))
sceneInteractor.setTransitionState(sceneTransitionStateFlow)
- deviceEntryInteractor =
- sceneTestUtils.deviceEntryInteractor(
- authenticationInteractor = sceneTestUtils.authenticationInteractor(),
- sceneInteractor = sceneInteractor,
- )
+ deviceEntryInteractor = sceneTestUtils.deviceEntryInteractor()
mSetFlagsRule.disableFlags(FLAG_SIDEFPS_CONTROLLER_REFACTOR)
underTest =
@@ -253,7 +249,7 @@
falsingManager,
userSwitcherController,
featureFlags,
- sceneTestUtils.sceneContainerFlags,
+ sceneTestUtils.fakeSceneContainerFlags,
globalSettings,
sessionTracker,
Optional.of(sideFpsController),
@@ -791,6 +787,7 @@
@Test
fun dismissesKeyguard_whenSceneChangesToGone() =
sceneTestUtils.testScope.runTest {
+ sceneTestUtils.fakeSceneContainerFlags.enabled = true
// Upon init, we have never dismisses the keyguard.
underTest.onInit()
runCurrent()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/authentication/data/repository/AuthenticationRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/authentication/data/repository/AuthenticationRepositoryTest.kt
index b9759cc..b4d4e1f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/authentication/data/repository/AuthenticationRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/authentication/data/repository/AuthenticationRepositoryTest.kt
@@ -83,7 +83,7 @@
AuthenticationRepositoryImpl(
applicationScope = testScope.backgroundScope,
backgroundDispatcher = testUtils.testDispatcher,
- flags = testUtils.sceneContainerFlags,
+ flags = testUtils.fakeSceneContainerFlags,
clock = clock,
getSecurityMode = getSecurityMode,
userRepository = userRepository,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
index b0beab9..f7743e2 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
@@ -38,6 +38,7 @@
import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.ActivityLaunchAnimator
+import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor
import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams
import com.android.systemui.biometrics.ui.viewmodel.DefaultUdfpsTouchOverlayViewModel
import com.android.systemui.biometrics.ui.viewmodel.DeviceEntryUdfpsTouchOverlayViewModel
@@ -118,6 +119,7 @@
@Mock private lateinit var keyguardTransitionInteractor: KeyguardTransitionInteractor
@Mock private lateinit var shadeInteractor: ShadeInteractor
@Captor private lateinit var layoutParamsCaptor: ArgumentCaptor<WindowManager.LayoutParams>
+ @Mock private lateinit var udfpsOverlayInteractor: UdfpsOverlayInteractor
private val onTouch = { _: View, _: MotionEvent, _: Boolean -> true }
private var overlayParams: UdfpsOverlayParams = UdfpsOverlayParams()
@@ -174,7 +176,8 @@
mSelectedUserInteractor,
{ deviceEntryUdfpsTouchOverlayViewModel },
{ defaultUdfpsTouchOverlayViewModel },
- shadeInteractor
+ shadeInteractor,
+ udfpsOverlayInteractor,
)
block()
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
index a59a4b8..90c3c14 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
@@ -76,6 +76,7 @@
import com.android.systemui.Flags;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.animation.ActivityLaunchAnimator;
+import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor;
import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams;
import com.android.systemui.biometrics.udfps.InteractionEvent;
import com.android.systemui.biometrics.udfps.NormalizedTouchData;
@@ -214,6 +215,8 @@
@Mock
private AlternateBouncerInteractor mAlternateBouncerInteractor;
@Mock
+ private UdfpsOverlayInteractor mUdfpsOverlayInteractor;
+ @Mock
private UdfpsKeyguardAccessibilityDelegate mUdfpsKeyguardAccessibilityDelegate;
@Mock
private SelectedUserInteractor mSelectedUserInteractor;
@@ -342,8 +345,8 @@
mFpsUnlockTracker,
mKeyguardTransitionInteractor,
mDeviceEntryUdfpsTouchOverlayViewModel,
- mDefaultUdfpsTouchOverlayViewModel
-
+ mDefaultUdfpsTouchOverlayViewModel,
+ mUdfpsOverlayInteractor
);
verify(mFingerprintManager).setUdfpsOverlayController(mOverlayCaptor.capture());
mOverlayController = mOverlayCaptor.getValue();
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerBaseTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerBaseTest.java
index 13b53a8..7d9c2f9 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerBaseTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerBaseTest.java
@@ -27,6 +27,7 @@
import com.android.systemui.Flags;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.animation.ActivityLaunchAnimator;
+import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor;
import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor;
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor;
import com.android.systemui.dump.DumpManager;
@@ -76,6 +77,7 @@
protected @Mock UdfpsKeyguardAccessibilityDelegate mUdfpsKeyguardAccessibilityDelegate;
protected @Mock SelectedUserInteractor mSelectedUserInteractor;
protected @Mock KeyguardTransitionInteractor mKeyguardTransitionInteractor;
+ protected @Mock UdfpsOverlayInteractor mUdfpsOverlayInteractor;
protected FakeFeatureFlags mFeatureFlags = new FakeFeatureFlags();
@@ -152,7 +154,8 @@
mUdfpsKeyguardAccessibilityDelegate,
mSelectedUserInteractor,
mKeyguardTransitionInteractor,
- mShadeInteractor);
+ mShadeInteractor,
+ mUdfpsOverlayInteractor);
return controller;
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt
index 335ac9d..0e257bc 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt
@@ -24,6 +24,7 @@
import com.android.systemui.biometrics.UdfpsKeyguardViewLegacy.ANIMATE_APPEAR_ON_SCREEN_OFF
import com.android.systemui.biometrics.UdfpsKeyguardViewLegacy.ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN
import com.android.systemui.biometrics.data.repository.FakeFingerprintPropertyRepository
+import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor
import com.android.systemui.bouncer.data.repository.KeyguardBouncerRepository
import com.android.systemui.bouncer.data.repository.KeyguardBouncerRepositoryImpl
import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
@@ -32,6 +33,7 @@
import com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants
import com.android.systemui.bouncer.ui.BouncerView
import com.android.systemui.classifier.FalsingCollector
+import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor
import com.android.systemui.keyguard.DismissCallbackRegistry
import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository
@@ -45,9 +47,11 @@
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.StatusBarState
import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.time.FakeSystemClock
import com.android.systemui.util.time.SystemClock
+import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
@@ -130,6 +134,13 @@
repository = transitionRepository,
)
.keyguardTransitionInteractor
+ mUdfpsOverlayInteractor =
+ UdfpsOverlayInteractor(
+ context,
+ mock(AuthController::class.java),
+ mock(SelectedUserInteractor::class.java),
+ testScope.backgroundScope,
+ )
return createUdfpsKeyguardViewController(/* useModernBouncer */ true)
}
@@ -239,6 +250,31 @@
}
@Test
+ fun shouldHandleTouchesChange() =
+ testScope.runTest {
+ val shouldHandleTouches by collectLastValue(mUdfpsOverlayInteractor.shouldHandleTouches)
+
+ // GIVEN view is attached + on the keyguard
+ mController.onViewAttached()
+ captureStatusBarStateListeners()
+ sendStatusBarStateChanged(StatusBarState.KEYGUARD)
+ whenever(mView.setPauseAuth(true)).thenReturn(true)
+ whenever(mView.unpausedAlpha).thenReturn(0)
+
+ // WHEN panelViewExpansion changes to expanded
+ val job = mController.listenForBouncerExpansion(this)
+ keyguardBouncerRepository.setPrimaryShow(true)
+ keyguardBouncerRepository.setPanelExpansion(KeyguardBouncerConstants.EXPANSION_VISIBLE)
+ runCurrent()
+
+ // THEN UDFPS auth is paused and should not handle touches
+ assertThat(mController.shouldPauseAuth()).isTrue()
+ assertThat(shouldHandleTouches!!).isFalse()
+
+ job.cancel()
+ }
+
+ @Test
fun fadeFromDialogSuggestedAlpha() =
testScope.runTest {
// GIVEN view is attached and status bar expansion is 1f
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/BouncerActionButtonInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/BouncerActionButtonInteractorTest.kt
index 67ce86b..63581b3 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/BouncerActionButtonInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/BouncerActionButtonInteractorTest.kt
@@ -16,27 +16,25 @@
package com.android.systemui.bouncer.domain.interactor
-import android.app.ActivityTaskManager
import android.telecom.TelecomManager
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.app.activityTaskManager
import com.android.internal.R
+import com.android.internal.logging.fakeMetricsLogger
import com.android.internal.logging.nano.MetricsProto
-import com.android.internal.logging.testing.FakeMetricsLogger
-import com.android.internal.util.EmergencyAffordanceManager
+import com.android.internal.util.emergencyAffordanceManager
import com.android.systemui.SysuiTestCase
import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.flags.Flags.REFACTOR_GETCURRENTUSER
-import com.android.systemui.log.table.TableLogBuffer
import com.android.systemui.scene.SceneTestUtils
import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionsRepository
-import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy
import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import com.android.systemui.util.mockito.whenever
+import com.android.telecom.telecomManager
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
@@ -53,27 +51,26 @@
@RunWith(AndroidJUnit4::class)
class BouncerActionButtonInteractorTest : SysuiTestCase() {
- @Mock private lateinit var activityTaskManager: ActivityTaskManager
- @Mock private lateinit var emergencyAffordanceManager: EmergencyAffordanceManager
@Mock private lateinit var selectedUserInteractor: SelectedUserInteractor
- @Mock private lateinit var tableLogger: TableLogBuffer
@Mock private lateinit var telecomManager: TelecomManager
- private lateinit var utils: SceneTestUtils
- private lateinit var testScope: TestScope
+ private val utils = SceneTestUtils(this)
+ private val testScope = utils.testScope
+ private val metricsLogger = utils.kosmos.fakeMetricsLogger
+ private val activityTaskManager = utils.kosmos.activityTaskManager
+ private val emergencyAffordanceManager = utils.kosmos.emergencyAffordanceManager
+
private lateinit var mobileConnectionsRepository: FakeMobileConnectionsRepository
- private val metricsLogger = FakeMetricsLogger()
private var currentUserId: Int = 0
private var needsEmergencyAffordance = true
- private lateinit var underTest: BouncerActionButtonInteractor
-
@Before
fun setUp() {
- utils = SceneTestUtils(this)
- testScope = utils.testScope
MockitoAnnotations.initMocks(this)
+ utils.fakeSceneContainerFlags.enabled = true
+
+ mobileConnectionsRepository = utils.mobileConnectionsRepository
overrideResource(R.string.lockscreen_emergency_call, MESSAGE_EMERGENCY_CALL)
overrideResource(R.string.lockscreen_return_to_call, MESSAGE_RETURN_TO_CALL)
@@ -86,34 +83,18 @@
.thenReturn(needsEmergencyAffordance)
whenever(telecomManager.isInCall).thenReturn(false)
- utils.featureFlags.set(REFACTOR_GETCURRENTUSER, true)
-
- mobileConnectionsRepository =
- FakeMobileConnectionsRepository(FakeMobileMappingsProxy(), tableLogger)
+ utils.fakeFeatureFlags.set(REFACTOR_GETCURRENTUSER, true)
utils.telephonyRepository.setHasTelephonyRadio(true)
- underTest =
- utils.bouncerActionButtonInteractor(
- mobileConnectionsRepository = mobileConnectionsRepository,
- activityTaskManager = activityTaskManager,
- telecomManager = telecomManager,
- emergencyAffordanceManager = emergencyAffordanceManager,
- metricsLogger = metricsLogger,
- )
+ utils.kosmos.telecomManager = telecomManager
}
@Test
fun noTelephonyRadio_noButton() =
testScope.runTest {
utils.telephonyRepository.setHasTelephonyRadio(false)
- underTest =
- utils.bouncerActionButtonInteractor(
- mobileConnectionsRepository = mobileConnectionsRepository,
- activityTaskManager = activityTaskManager,
- telecomManager = telecomManager,
- )
-
+ val underTest = utils.bouncerActionButtonInteractor()
val actionButton by collectLastValue(underTest.actionButton)
assertThat(actionButton).isNull()
}
@@ -121,12 +102,8 @@
@Test
fun noTelecomManager_noButton() =
testScope.runTest {
- underTest =
- utils.bouncerActionButtonInteractor(
- mobileConnectionsRepository = mobileConnectionsRepository,
- activityTaskManager = activityTaskManager,
- telecomManager = null,
- )
+ utils.kosmos.telecomManager = null
+ val underTest = utils.bouncerActionButtonInteractor()
val actionButton by collectLastValue(underTest.actionButton)
assertThat(actionButton).isNull()
}
@@ -134,6 +111,7 @@
@Test
fun duringCall_returnToCallButton() =
testScope.runTest {
+ val underTest = utils.bouncerActionButtonInteractor()
val actionButton by collectLastValue(underTest.actionButton)
utils.telephonyRepository.setIsInCall(true)
@@ -143,6 +121,7 @@
assertThat(actionButton?.onLongClick).isNull()
actionButton?.onClick?.invoke()
+ runCurrent()
assertThat(metricsLogger.logs.size).isEqualTo(1)
assertThat(metricsLogger.logs.element().category)
@@ -154,6 +133,7 @@
@Test
fun noCall_secureAuthMethod_emergencyCallButton() =
testScope.runTest {
+ val underTest = utils.bouncerActionButtonInteractor()
val actionButton by collectLastValue(underTest.actionButton)
mobileConnectionsRepository.isAnySimSecure.value = false
utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
@@ -165,6 +145,7 @@
assertThat(actionButton?.onLongClick).isNotNull()
actionButton?.onClick?.invoke()
+ runCurrent()
assertThat(metricsLogger.logs.size).isEqualTo(1)
assertThat(metricsLogger.logs.element().category)
@@ -182,10 +163,12 @@
@Test
fun noCall_insecureAuthMethodButSecureSim_emergencyCallButton() =
testScope.runTest {
+ val underTest = utils.bouncerActionButtonInteractor()
val actionButton by collectLastValue(underTest.actionButton)
mobileConnectionsRepository.isAnySimSecure.value = true
utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.None)
utils.telephonyRepository.setIsInCall(false)
+ runCurrent()
assertThat(actionButton).isNotNull()
assertThat(actionButton?.label).isEqualTo(MESSAGE_EMERGENCY_CALL)
@@ -196,6 +179,7 @@
@Test
fun noCall_insecure_noButton() =
testScope.runTest {
+ val underTest = utils.bouncerActionButtonInteractor()
val actionButton by collectLastValue(underTest.actionButton)
mobileConnectionsRepository.isAnySimSecure.value = false
utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.None)
@@ -207,6 +191,7 @@
@Test
fun noCall_simSecureButEmergencyNotSupported_noButton() =
testScope.runTest {
+ val underTest = utils.bouncerActionButtonInteractor()
val actionButton by collectLastValue(underTest.actionButton)
mobileConnectionsRepository.isAnySimSecure.value = true
overrideResource(R.bool.config_enable_emergency_call_while_sim_locked, false)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt
index 93ba6a4..4b6199b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt
@@ -25,7 +25,8 @@
import com.android.systemui.authentication.shared.model.AuthenticationPatternCoordinate
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.coroutines.collectValues
-import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor
+import com.android.systemui.deviceentry.domain.interactor.deviceEntryFaceAuthInteractor
+import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFaceAuthRepository
import com.android.systemui.res.R
import com.android.systemui.scene.SceneTestUtils
import com.google.common.truth.Truth.assertThat
@@ -37,8 +38,6 @@
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
-import org.mockito.Mock
-import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
@OptIn(ExperimentalCoroutinesApi::class)
@@ -46,9 +45,7 @@
@RunWith(AndroidJUnit4::class)
class BouncerInteractorTest : SysuiTestCase() {
- @Mock private lateinit var mDeviceEntryFaceAuthInteractor: DeviceEntryFaceAuthInteractor
-
- private val utils = SceneTestUtils(this)
+ private val utils = SceneTestUtils(this).apply { fakeSceneContainerFlags.enabled = true }
private val testScope = utils.testScope
private val authenticationInteractor = utils.authenticationInteractor()
@@ -57,6 +54,7 @@
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
+
overrideResource(R.string.keyguard_enter_your_pin, MESSAGE_ENTER_YOUR_PIN)
overrideResource(R.string.keyguard_enter_your_password, MESSAGE_ENTER_YOUR_PASSWORD)
overrideResource(R.string.keyguard_enter_your_pattern, MESSAGE_ENTER_YOUR_PATTERN)
@@ -64,11 +62,7 @@
overrideResource(R.string.kg_wrong_password, MESSAGE_WRONG_PASSWORD)
overrideResource(R.string.kg_wrong_pattern, MESSAGE_WRONG_PATTERN)
- underTest =
- utils.bouncerInteractor(
- authenticationInteractor = authenticationInteractor,
- deviceEntryFaceAuthInteractor = mDeviceEntryFaceAuthInteractor,
- )
+ underTest = utils.bouncerInteractor()
}
@Test
@@ -305,8 +299,16 @@
@Test
fun intentionalUserInputEvent_notifiesFaceAuthInteractor() =
testScope.runTest {
+ val isFaceAuthRunning by
+ collectLastValue(utils.kosmos.fakeDeviceEntryFaceAuthRepository.isAuthRunning)
+ utils.kosmos.deviceEntryFaceAuthInteractor.onDeviceLifted()
+ runCurrent()
+ assertThat(isFaceAuthRunning).isTrue()
+
underTest.onIntentionalUserInput()
- verify(mDeviceEntryFaceAuthInteractor).onPrimaryBouncerUserInput()
+ runCurrent()
+
+ assertThat(isFaceAuthRunning).isFalse()
}
companion object {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModelTest.kt
index 2f0843b..3043a71 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModelTest.kt
@@ -35,10 +35,7 @@
private val utils = SceneTestUtils(this)
private val testScope = utils.testScope
- private val bouncerInteractor =
- utils.bouncerInteractor(
- authenticationInteractor = utils.authenticationInteractor(),
- )
+ private val bouncerInteractor = utils.bouncerInteractor()
private val underTest =
PinBouncerViewModel(
applicationContext = context,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt
index 47bbe6f4..4b1f9fe 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt
@@ -41,6 +41,7 @@
import kotlinx.coroutines.test.currentTime
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
+import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -52,15 +53,14 @@
private val utils = SceneTestUtils(this)
private val testScope = utils.testScope
private val authenticationInteractor = utils.authenticationInteractor()
- private val bouncerInteractor =
- utils.bouncerInteractor(
- authenticationInteractor = authenticationInteractor,
- )
- private val underTest =
- utils.bouncerViewModel(
- bouncerInteractor = bouncerInteractor,
- authenticationInteractor = authenticationInteractor,
- )
+ private val bouncerInteractor = utils.bouncerInteractor()
+ private lateinit var underTest: BouncerViewModel
+
+ @Before
+ fun setUp() {
+ utils.fakeSceneContainerFlags.enabled = true
+ underTest = utils.bouncerViewModel()
+ }
@Test
fun authMethod_nonNullForSecureMethods_nullForNotSecureMethods() =
@@ -228,13 +228,13 @@
fun isSideBySideSupported() =
testScope.runTest {
val isSideBySideSupported by collectLastValue(underTest.isSideBySideSupported)
- utils.featureFlags.set(Flags.FULL_SCREEN_USER_SWITCHER, true)
+ utils.fakeFeatureFlags.set(Flags.FULL_SCREEN_USER_SWITCHER, true)
utils.authenticationRepository.setAuthenticationMethod(Pin)
assertThat(isSideBySideSupported).isTrue()
utils.authenticationRepository.setAuthenticationMethod(Password)
assertThat(isSideBySideSupported).isTrue()
- utils.featureFlags.set(Flags.FULL_SCREEN_USER_SWITCHER, false)
+ utils.fakeFeatureFlags.set(Flags.FULL_SCREEN_USER_SWITCHER, false)
utils.authenticationRepository.setAuthenticationMethod(Pin)
assertThat(isSideBySideSupported).isTrue()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt
index 64e6e57..5c5632f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt
@@ -47,16 +47,8 @@
private val testScope = utils.testScope
private val authenticationInteractor = utils.authenticationInteractor()
private val sceneInteractor = utils.sceneInteractor()
- private val bouncerInteractor =
- utils.bouncerInteractor(
- authenticationInteractor = authenticationInteractor,
- )
- private val bouncerViewModel =
- utils.bouncerViewModel(
- bouncerInteractor = bouncerInteractor,
- authenticationInteractor = authenticationInteractor,
- actionButtonInteractor = utils.bouncerActionButtonInteractor(),
- )
+ private val bouncerInteractor = utils.bouncerInteractor()
+ private val bouncerViewModel = utils.bouncerViewModel()
private val isInputEnabled = MutableStateFlow(true)
private val underTest =
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt
index 83d1938..9ee344a 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt
@@ -48,16 +48,8 @@
private val testScope = utils.testScope
private val authenticationInteractor = utils.authenticationInteractor()
private val sceneInteractor = utils.sceneInteractor()
- private val bouncerInteractor =
- utils.bouncerInteractor(
- authenticationInteractor = authenticationInteractor,
- )
- private val bouncerViewModel =
- utils.bouncerViewModel(
- bouncerInteractor = bouncerInteractor,
- authenticationInteractor = authenticationInteractor,
- actionButtonInteractor = utils.bouncerActionButtonInteractor(),
- )
+ private val bouncerInteractor = utils.bouncerInteractor()
+ private val bouncerViewModel = utils.bouncerViewModel()
private val underTest =
PatternBouncerViewModel(
applicationContext = context,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt
index db98d76..75e372f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt
@@ -47,16 +47,8 @@
private val testScope = utils.testScope
private val sceneInteractor = utils.sceneInteractor()
private val authenticationInteractor = utils.authenticationInteractor()
- private val bouncerInteractor =
- utils.bouncerInteractor(
- authenticationInteractor = authenticationInteractor,
- )
- private val bouncerViewModel =
- utils.bouncerViewModel(
- bouncerInteractor = bouncerInteractor,
- authenticationInteractor = authenticationInteractor,
- actionButtonInteractor = utils.bouncerActionButtonInteractor(),
- )
+ private val bouncerInteractor = utils.bouncerInteractor()
+ private val bouncerViewModel = utils.bouncerViewModel()
private val underTest =
PinBouncerViewModel(
applicationContext = context,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt
index a776062..804c052 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt
@@ -34,6 +34,7 @@
import com.android.systemui.communal.widgets.WidgetInteractionHandler
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
+import com.android.systemui.media.controls.ui.MediaHierarchyManager
import com.android.systemui.media.controls.ui.MediaHost
import com.android.systemui.smartspace.data.repository.FakeSmartspaceRepository
import com.android.systemui.util.mockito.mock
@@ -48,6 +49,7 @@
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Mockito
+import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
@OptIn(ExperimentalCoroutinesApi::class)
@@ -92,6 +94,13 @@
}
@Test
+ fun init_initsMediaHost() =
+ testScope.runTest {
+ // MediaHost is initialized as soon as the class is created.
+ verify(mediaHost).init(MediaHierarchyManager.LOCATION_COMMUNAL_HUB)
+ }
+
+ @Test
fun tutorial_tutorialNotCompletedAndKeyguardVisible_showTutorialContent() =
testScope.runTest {
// Keyguard showing, and tutorial not started.
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorTest.kt
index ea19cb7..929e879 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorTest.kt
@@ -23,9 +23,8 @@
import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.coroutines.collectValues
-import com.android.systemui.deviceentry.data.repository.FakeDeviceEntryRepository
-import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFaceAuthRepository
-import com.android.systemui.keyguard.data.repository.FakeTrustRepository
+import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFaceAuthRepository
+import com.android.systemui.keyguard.data.repository.fakeTrustRepository
import com.android.systemui.scene.SceneTestUtils
import com.android.systemui.scene.shared.model.SceneKey
import com.android.systemui.scene.shared.model.SceneModel
@@ -33,6 +32,7 @@
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
+import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -43,19 +43,17 @@
private val utils = SceneTestUtils(this)
private val testScope = utils.testScope
- private val repository: FakeDeviceEntryRepository = utils.deviceEntryRepository
- private val faceAuthRepository = FakeDeviceEntryFaceAuthRepository()
- private val trustRepository = FakeTrustRepository()
+ private val faceAuthRepository = utils.kosmos.fakeDeviceEntryFaceAuthRepository
+ private val trustRepository = utils.kosmos.fakeTrustRepository
private val sceneInteractor = utils.sceneInteractor()
private val authenticationInteractor = utils.authenticationInteractor()
- private val underTest =
- utils.deviceEntryInteractor(
- repository = repository,
- authenticationInteractor = authenticationInteractor,
- sceneInteractor = sceneInteractor,
- faceAuthRepository = faceAuthRepository,
- trustRepository = trustRepository,
- )
+ private lateinit var underTest: DeviceEntryInteractor
+
+ @Before
+ fun setUp() {
+ utils.fakeSceneContainerFlags.enabled = true
+ underTest = utils.deviceEntryInteractor()
+ }
@Test
fun canSwipeToEnter_startsNull() =
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt
index 11939c1..8933d2c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt
@@ -63,7 +63,7 @@
repository = repository,
commandQueue = commandQueue,
powerInteractor = PowerInteractorFactory.create().powerInteractor,
- sceneContainerFlags = testUtils.sceneContainerFlags,
+ sceneContainerFlags = testUtils.fakeSceneContainerFlags,
bouncerRepository = bouncerRepository,
configurationInteractor = ConfigurationInteractor(FakeConfigurationRepository()),
shadeRepository = shadeRepository,
@@ -183,6 +183,7 @@
@Test
fun animationDozingTransitions() =
testScope.runTest {
+ testUtils.fakeSceneContainerFlags.enabled = true
val isAnimate by collectLastValue(underTest.animateDozingTransitions)
underTest.setAnimateDozingTransitions(true)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt
index 74d309c..04e90c8 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt
@@ -87,11 +87,7 @@
private fun createLockscreenSceneViewModel(): LockscreenSceneViewModel {
return LockscreenSceneViewModel(
applicationScope = testScope.backgroundScope,
- deviceEntryInteractor =
- utils.deviceEntryInteractor(
- authenticationInteractor = utils.authenticationInteractor(),
- sceneInteractor = utils.sceneInteractor(),
- ),
+ deviceEntryInteractor = utils.deviceEntryInteractor(),
communalInteractor = utils.communalInteractor(),
longPress =
KeyguardLongPressViewModel(
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
index 530d127d..ecc2ef1 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
@@ -24,6 +24,7 @@
import androidx.test.filters.SmallTest
import com.android.internal.R
import com.android.internal.util.EmergencyAffordanceManager
+import com.android.internal.util.emergencyAffordanceManager
import com.android.systemui.SysuiTestCase
import com.android.systemui.authentication.data.repository.FakeAuthenticationRepository
import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
@@ -35,13 +36,11 @@
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.ui.viewmodel.KeyguardLongPressViewModel
import com.android.systemui.keyguard.ui.viewmodel.LockscreenSceneViewModel
-import com.android.systemui.log.table.TableLogBuffer
import com.android.systemui.media.controls.pipeline.MediaDataManager
import com.android.systemui.media.controls.ui.MediaHost
import com.android.systemui.model.SysUiState
import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAsleepForTest
import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAwakeForTest
-import com.android.systemui.power.domain.interactor.PowerInteractorFactory
import com.android.systemui.qs.ui.adapter.FakeQSSceneAdapter
import com.android.systemui.scene.domain.startable.SceneContainerStartable
import com.android.systemui.scene.shared.model.ObservableTransitionState
@@ -61,6 +60,7 @@
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
+import com.android.telecom.telecomManager
import com.google.common.truth.Truth.assertThat
import com.google.common.truth.Truth.assertWithMessage
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -101,27 +101,12 @@
@RunWith(AndroidJUnit4::class)
class SceneFrameworkIntegrationTest : SysuiTestCase() {
- @Mock private lateinit var emergencyAffordanceManager: EmergencyAffordanceManager
- @Mock private lateinit var tableLogger: TableLogBuffer
- @Mock private lateinit var telecomManager: TelecomManager
-
- private val utils = SceneTestUtils(this)
+ private val utils = SceneTestUtils(this).apply { fakeSceneContainerFlags.enabled = true }
private val testScope = utils.testScope
private val sceneContainerConfig = utils.fakeSceneContainerConfig()
- private val sceneRepository =
- utils.fakeSceneContainerRepository(
- containerConfig = sceneContainerConfig,
- )
- private val sceneInteractor =
- utils.sceneInteractor(
- repository = sceneRepository,
- )
+ private val sceneInteractor = utils.sceneInteractor()
private val authenticationInteractor = utils.authenticationInteractor()
- private val deviceEntryInteractor =
- utils.deviceEntryInteractor(
- authenticationInteractor = authenticationInteractor,
- sceneInteractor = sceneInteractor,
- )
+ private val deviceEntryInteractor = utils.deviceEntryInteractor()
private val communalInteractor = utils.communalInteractor()
private val transitionState =
@@ -135,10 +120,7 @@
)
.apply { setTransitionState(transitionState) }
- private val bouncerInteractor =
- utils.bouncerInteractor(
- authenticationInteractor = authenticationInteractor,
- )
+ private val bouncerInteractor = utils.bouncerInteractor()
private lateinit var mobileConnectionsRepository: FakeMobileConnectionsRepository
private lateinit var bouncerActionButtonInteractor: BouncerActionButtonInteractor
@@ -170,19 +152,15 @@
FakeMobileConnectionsRepository(),
),
constants = mock(),
- utils.featureFlags,
+ utils.fakeFeatureFlags,
scope = testScope.backgroundScope,
)
private lateinit var shadeHeaderViewModel: ShadeHeaderViewModel
private lateinit var shadeSceneViewModel: ShadeSceneViewModel
- private val keyguardRepository = utils.keyguardRepository
- private val keyguardInteractor =
- utils.keyguardInteractor(
- repository = keyguardRepository,
- )
- private val powerInteractor = PowerInteractorFactory.create().powerInteractor
+ private val keyguardInteractor = utils.keyguardInteractor()
+ private val powerInteractor = utils.powerInteractor()
private var bouncerSceneJob: Job? = null
@@ -191,21 +169,26 @@
@Mock private lateinit var mediaDataManager: MediaDataManager
@Mock private lateinit var mediaHost: MediaHost
+ private lateinit var emergencyAffordanceManager: EmergencyAffordanceManager
+ private lateinit var telecomManager: TelecomManager
+
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
+
overrideResource(R.bool.config_enable_emergency_call_while_sim_locked, true)
+ telecomManager = checkNotNull(utils.kosmos.telecomManager)
whenever(telecomManager.isInCall).thenReturn(false)
+ emergencyAffordanceManager = utils.kosmos.emergencyAffordanceManager
whenever(emergencyAffordanceManager.needsEmergencyAffordance()).thenReturn(true)
- utils.featureFlags.apply {
+ utils.fakeFeatureFlags.apply {
set(Flags.NEW_NETWORK_SLICE_UI, false)
set(Flags.REFACTOR_GETCURRENTUSER, true)
}
- mobileConnectionsRepository =
- FakeMobileConnectionsRepository(FakeMobileMappingsProxy(), tableLogger)
- mobileConnectionsRepository.isAnySimSecure.value = true
+ mobileConnectionsRepository = utils.mobileConnectionsRepository
+ mobileConnectionsRepository.isAnySimSecure.value = false
utils.telephonyRepository.apply {
setHasTelephonyRadio(true)
@@ -213,18 +196,8 @@
setIsInCall(false)
}
- bouncerActionButtonInteractor =
- utils.bouncerActionButtonInteractor(
- mobileConnectionsRepository = mobileConnectionsRepository,
- telecomManager = telecomManager,
- emergencyAffordanceManager = emergencyAffordanceManager,
- )
- bouncerViewModel =
- utils.bouncerViewModel(
- bouncerInteractor = bouncerInteractor,
- authenticationInteractor = authenticationInteractor,
- actionButtonInteractor = bouncerActionButtonInteractor,
- )
+ bouncerActionButtonInteractor = utils.bouncerActionButtonInteractor()
+ bouncerViewModel = utils.bouncerViewModel()
shadeHeaderViewModel =
ShadeHeaderViewModel(
@@ -257,7 +230,7 @@
sceneInteractor = sceneInteractor,
deviceEntryInteractor = deviceEntryInteractor,
keyguardInteractor = keyguardInteractor,
- flags = utils.sceneContainerFlags,
+ flags = utils.fakeSceneContainerFlags,
sysUiState = sysUiState,
displayId = displayTracker.defaultDisplayId,
sceneLogger = mock(),
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryTest.kt
index ddeb05b..339d026 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryTest.kt
@@ -39,7 +39,7 @@
@RunWith(AndroidJUnit4::class)
class SceneContainerRepositoryTest : SysuiTestCase() {
- private val utils = SceneTestUtils(this)
+ private val utils = SceneTestUtils(this).apply { fakeSceneContainerFlags.enabled = true }
private val testScope = utils.testScope
@Test
@@ -72,7 +72,7 @@
@Test(expected = IllegalStateException::class)
fun setDesiredScene_noSuchSceneInContainer_throws() {
utils.kosmos.sceneKeys = listOf(SceneKey.QuickSettings, SceneKey.Lockscreen)
- val underTest = utils.fakeSceneContainerRepository(utils.fakeSceneContainerConfig())
+ val underTest = utils.fakeSceneContainerRepository()
underTest.setDesiredScene(SceneModel(SceneKey.Shade))
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt
index 7f4bbbe..486f7ab 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt
@@ -28,6 +28,7 @@
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.test.runTest
+import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -37,8 +38,14 @@
private val utils = SceneTestUtils(this)
private val testScope = utils.testScope
- private val repository = utils.fakeSceneContainerRepository()
- private val underTest = utils.sceneInteractor(repository = repository)
+
+ private lateinit var underTest: SceneInteractor
+
+ @Before
+ fun setUp() {
+ utils.fakeSceneContainerFlags.enabled = true
+ underTest = utils.sceneInteractor()
+ }
@Test
fun allSceneKeys() {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
index dd22976..5fe4ca1 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
@@ -28,7 +28,7 @@
import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFaceAuthRepository
+import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFaceAuthRepository
import com.android.systemui.model.SysUiState
import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAsleepForTest
import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAwakeForTest
@@ -68,17 +68,11 @@
private val utils = SceneTestUtils(this)
private val testScope = utils.testScope
private val sceneInteractor = utils.sceneInteractor()
- private val sceneContainerFlags = utils.sceneContainerFlags
+ private val sceneContainerFlags = utils.fakeSceneContainerFlags
private val authenticationInteractor = utils.authenticationInteractor()
- private val bouncerInteractor =
- utils.bouncerInteractor(authenticationInteractor = authenticationInteractor)
- private val faceAuthRepository = FakeDeviceEntryFaceAuthRepository()
- private val deviceEntryInteractor =
- utils.deviceEntryInteractor(
- faceAuthRepository = faceAuthRepository,
- authenticationInteractor = authenticationInteractor,
- sceneInteractor = sceneInteractor,
- )
+ private val bouncerInteractor = utils.bouncerInteractor()
+ private val faceAuthRepository = utils.kosmos.fakeDeviceEntryFaceAuthRepository
+ private val deviceEntryInteractor = utils.deviceEntryInteractor()
private val keyguardInteractor = utils.keyguardInteractor()
private val sysUiState: SysUiState = mock()
private val falsingCollector: FalsingCollector = mock()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt
index c89cd9e..3a4ee64 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt
@@ -28,6 +28,7 @@
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runTest
+import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -37,11 +38,17 @@
private val utils = SceneTestUtils(this)
private val interactor = utils.sceneInteractor()
- private val underTest =
- SceneContainerViewModel(
- sceneInteractor = interactor,
- falsingInteractor = utils.falsingInteractor(),
- )
+ private lateinit var underTest: SceneContainerViewModel
+
+ @Before
+ fun setUp() {
+ utils.fakeSceneContainerFlags.enabled = true
+ underTest =
+ SceneContainerViewModel(
+ sceneInteractor = interactor,
+ falsingInteractor = utils.falsingInteractor(),
+ )
+ }
@Test
fun isVisible() = runTest {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt
index 1d2497d..a8133a3a 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt
@@ -56,12 +56,7 @@
private val utils = SceneTestUtils(this)
private val testScope = utils.testScope
private val sceneInteractor = utils.sceneInteractor()
- private val authenticationInteractor = utils.authenticationInteractor()
- private val deviceEntryInteractor =
- utils.deviceEntryInteractor(
- authenticationInteractor = authenticationInteractor,
- sceneInteractor = sceneInteractor,
- )
+ private val deviceEntryInteractor = utils.deviceEntryInteractor()
private val mobileIconsInteractor = FakeMobileIconsInteractor(FakeMobileMappingsProxy(), mock())
private val flags = FakeFeatureFlagsClassic().also { it.set(Flags.NEW_NETWORK_SLICE_UI, false) }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationStackAppearanceIntegrationTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationStackAppearanceIntegrationTest.kt
index 4cdb08a..607996d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationStackAppearanceIntegrationTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationStackAppearanceIntegrationTest.kt
@@ -27,8 +27,7 @@
import com.android.systemui.flags.fakeFeatureFlagsClassic
import com.android.systemui.kosmos.testScope
import com.android.systemui.scene.domain.interactor.sceneInteractor
-import com.android.systemui.scene.shared.flag.FakeSceneContainerFlags
-import com.android.systemui.scene.shared.flag.sceneContainerFlags
+import com.android.systemui.scene.shared.flag.fakeSceneContainerFlags
import com.android.systemui.scene.shared.model.ObservableTransitionState
import com.android.systemui.scene.shared.model.SceneKey
import com.android.systemui.scene.shared.model.SceneModel
@@ -50,7 +49,7 @@
private val kosmos =
testKosmos().apply {
- sceneContainerFlags = FakeSceneContainerFlags(enabled = true)
+ fakeSceneContainerFlags.enabled = true
fakeFeatureFlagsClassic.apply {
set(Flags.FULL_SCREEN_USER_SWITCHER, false)
set(Flags.NSSL_DEBUG_LINES, false)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/shared/model/SceneContainerConfigKosmos.kt b/packages/SystemUI/src/com/android/systemui/NoOpCoreStartable.kt
similarity index 74%
rename from packages/SystemUI/tests/utils/src/com/android/systemui/scene/shared/model/SceneContainerConfigKosmos.kt
rename to packages/SystemUI/src/com/android/systemui/NoOpCoreStartable.kt
index f9cdc1b..9f54c26 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/shared/model/SceneContainerConfigKosmos.kt
+++ b/packages/SystemUI/src/com/android/systemui/NoOpCoreStartable.kt
@@ -14,9 +14,9 @@
* limitations under the License.
*/
-package com.android.systemui.scene.shared.model
+package com.android.systemui
-import com.android.systemui.kosmos.Kosmos
-
-val Kosmos.sceneContainerConfig by
- Kosmos.Fixture { FakeSceneContainerConfigModule().sceneContainerConfig }
+/** A [CoreStartable] that does nothing. */
+class NoOpCoreStartable : CoreStartable {
+ override fun start() {}
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.kt
index 7d9ec08..f5603ed 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.kt
@@ -23,6 +23,7 @@
import androidx.lifecycle.repeatOnLifecycle
import com.android.app.animation.Interpolators
import com.android.systemui.Dumpable
+import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor
import com.android.systemui.dump.DumpManager
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.plugins.statusbar.StatusBarStateController
@@ -49,7 +50,8 @@
protected val statusBarStateController: StatusBarStateController,
protected val shadeInteractor: ShadeInteractor,
protected val dialogManager: SystemUIDialogManager,
- private val dumpManager: DumpManager
+ private val dumpManager: DumpManager,
+ private val udfpsOverlayInteractor: UdfpsOverlayInteractor,
) : ViewController<T>(view), Dumpable {
protected abstract val tag: String
@@ -130,11 +132,13 @@
override fun onViewAttached() {
dialogManager.registerListener(dialogListener)
dumpManager.registerDumpable(dumpTag, this)
+ udfpsOverlayInteractor.setHandleTouches(shouldHandle = true)
}
override fun onViewDetached() {
dialogManager.unregisterListener(dialogListener)
dumpManager.unregisterDumpable(dumpTag)
+ udfpsOverlayInteractor.setHandleTouches(shouldHandle = true)
}
/**
@@ -165,6 +169,7 @@
fun updatePauseAuth() {
if (view.setPauseAuth(shouldPauseAuth())) {
view.postInvalidate()
+ udfpsOverlayInteractor.setHandleTouches(shouldHandle = !shouldPauseAuth())
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpViewController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpViewController.kt
index e7b0d9f..e0455b5 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpViewController.kt
@@ -15,6 +15,7 @@
*/
package com.android.systemui.biometrics
+import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor
import com.android.systemui.dump.DumpManager
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.shade.domain.interactor.ShadeInteractor
@@ -28,13 +29,15 @@
statusBarStateController: StatusBarStateController,
shadeInteractor: ShadeInteractor,
systemUIDialogManager: SystemUIDialogManager,
- dumpManager: DumpManager
+ dumpManager: DumpManager,
+ udfpsOverlayInteractor: UdfpsOverlayInteractor,
) : UdfpsAnimationViewController<UdfpsBpView>(
view,
statusBarStateController,
shadeInteractor,
systemUIDialogManager,
- dumpManager
+ dumpManager,
+ udfpsOverlayInteractor,
) {
override val tag = "UdfpsBpViewController"
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index 81de0a2..66fe4b3 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -68,6 +68,7 @@
import com.android.systemui.Dumpable;
import com.android.systemui.animation.ActivityLaunchAnimator;
import com.android.systemui.biometrics.dagger.BiometricsBackground;
+import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor;
import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams;
import com.android.systemui.biometrics.udfps.InteractionEvent;
import com.android.systemui.biometrics.udfps.NormalizedTouchData;
@@ -171,6 +172,7 @@
@NonNull private final Lazy<DefaultUdfpsTouchOverlayViewModel>
mDefaultUdfpsTouchOverlayViewModel;
@NonNull private final AlternateBouncerInteractor mAlternateBouncerInteractor;
+ @NonNull private final UdfpsOverlayInteractor mUdfpsOverlayInteractor;
@NonNull private final InputManager mInputManager;
@NonNull private final UdfpsKeyguardAccessibilityDelegate mUdfpsKeyguardAccessibilityDelegate;
@NonNull private final SelectedUserInteractor mSelectedUserInteractor;
@@ -293,7 +295,8 @@
mSelectedUserInteractor,
mDeviceEntryUdfpsTouchOverlayViewModel,
mDefaultUdfpsTouchOverlayViewModel,
- mShadeInteractor
+ mShadeInteractor,
+ mUdfpsOverlayInteractor
)));
}
@@ -674,7 +677,8 @@
@NonNull FpsUnlockTracker fpsUnlockTracker,
@NonNull KeyguardTransitionInteractor keyguardTransitionInteractor,
Lazy<DeviceEntryUdfpsTouchOverlayViewModel> deviceEntryUdfpsTouchOverlayViewModel,
- Lazy<DefaultUdfpsTouchOverlayViewModel> defaultUdfpsTouchOverlayViewModel) {
+ Lazy<DefaultUdfpsTouchOverlayViewModel> defaultUdfpsTouchOverlayViewModel,
+ @NonNull UdfpsOverlayInteractor udfpsOverlayInteractor) {
mContext = context;
mExecution = execution;
mVibrator = vibrator;
@@ -715,6 +719,7 @@
mPrimaryBouncerInteractor = primaryBouncerInteractor;
mShadeInteractor = shadeInteractor;
mAlternateBouncerInteractor = alternateBouncerInteractor;
+ mUdfpsOverlayInteractor = udfpsOverlayInteractor;
mInputManager = inputManager;
mUdfpsKeyguardAccessibilityDelegate = udfpsKeyguardAccessibilityDelegate;
mSelectedUserInteractor = selectedUserInteractor;
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
index b94a177..4176083 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
@@ -45,6 +45,7 @@
import androidx.annotation.VisibleForTesting
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.systemui.animation.ActivityLaunchAnimator
+import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor
import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams
import com.android.systemui.biometrics.ui.binder.UdfpsTouchOverlayBinder
import com.android.systemui.biometrics.ui.view.UdfpsTouchOverlay
@@ -109,6 +110,7 @@
private val deviceEntryUdfpsTouchOverlayViewModel: Lazy<DeviceEntryUdfpsTouchOverlayViewModel>,
private val defaultUdfpsTouchOverlayViewModel: Lazy<DefaultUdfpsTouchOverlayViewModel>,
private val shadeInteractor: ShadeInteractor,
+ private val udfpsOverlayInteractor: UdfpsOverlayInteractor,
) {
private var overlayViewLegacy: UdfpsView? = null
private set
@@ -281,7 +283,8 @@
statusBarStateController,
shadeInteractor,
dialogManager,
- dumpManager
+ dumpManager,
+ udfpsOverlayInteractor,
)
}
REASON_AUTH_KEYGUARD -> {
@@ -306,6 +309,7 @@
selectedUserInteractor,
transitionInteractor,
shadeInteractor,
+ udfpsOverlayInteractor,
)
}
REASON_AUTH_BP -> {
@@ -315,7 +319,8 @@
statusBarStateController,
shadeInteractor,
dialogManager,
- dumpManager
+ dumpManager,
+ udfpsOverlayInteractor,
)
}
REASON_AUTH_OTHER,
@@ -325,7 +330,8 @@
statusBarStateController,
shadeInteractor,
dialogManager,
- dumpManager
+ dumpManager,
+ udfpsOverlayInteractor,
)
}
else -> {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmEmptyViewController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmEmptyViewController.kt
index ab3fbb1..cfbbc26 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmEmptyViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmEmptyViewController.kt
@@ -15,6 +15,7 @@
*/
package com.android.systemui.biometrics
+import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor
import com.android.systemui.dump.DumpManager
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.shade.domain.interactor.ShadeInteractor
@@ -30,13 +31,15 @@
statusBarStateController: StatusBarStateController,
shadeInteractor: ShadeInteractor,
systemUIDialogManager: SystemUIDialogManager,
- dumpManager: DumpManager
+ dumpManager: DumpManager,
+ udfpsOverlayInteractor: UdfpsOverlayInteractor,
) : UdfpsAnimationViewController<UdfpsFpmEmptyView>(
view,
statusBarStateController,
shadeInteractor,
systemUIDialogManager,
- dumpManager
+ dumpManager,
+ udfpsOverlayInteractor,
) {
override val tag = "UdfpsFpmOtherViewController"
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt
index 9f17024..7020d05 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt
@@ -27,6 +27,7 @@
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.systemui.animation.ActivityLaunchAnimator
import com.android.systemui.biometrics.UdfpsKeyguardViewLegacy.ANIMATE_APPEAR_ON_SCREEN_OFF
+import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor
import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
import com.android.systemui.dump.DumpManager
@@ -75,6 +76,7 @@
private val selectedUserInteractor: SelectedUserInteractor,
private val transitionInteractor: KeyguardTransitionInteractor,
shadeInteractor: ShadeInteractor,
+ udfpsOverlayInteractor: UdfpsOverlayInteractor,
) :
UdfpsAnimationViewController<UdfpsKeyguardViewLegacy>(
view,
@@ -82,6 +84,7 @@
shadeInteractor,
systemUIDialogManager,
dumpManager,
+ udfpsOverlayInteractor,
) {
private val uniqueIdentifier = this.toString()
private var showingUdfpsBouncer = false
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/LogContextInteractor.kt b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/LogContextInteractor.kt
index 863ba8d..70be0f2 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/LogContextInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/LogContextInteractor.kt
@@ -63,6 +63,9 @@
/** Current display state, defined as [AuthenticateOptions.DisplayState] */
val displayState: Flow<Int>
+ /** If touches on the fingerprint sensor should be ignored by the HAL. */
+ val isHardwareIgnoringTouches: Flow<Boolean>
+
/**
* Add a permanent context listener.
*
@@ -79,12 +82,11 @@
@Application private val applicationScope: CoroutineScope,
private val foldProvider: FoldStateProvider,
keyguardTransitionInteractor: KeyguardTransitionInteractor,
+ udfpsOverlayInteractor: UdfpsOverlayInteractor,
) : LogContextInteractor {
init {
- applicationScope.launch {
- foldProvider.start()
- }
+ applicationScope.launch { foldProvider.start() }
}
override val displayState =
@@ -102,6 +104,9 @@
}
}
+ override val isHardwareIgnoringTouches: Flow<Boolean> =
+ udfpsOverlayInteractor.shouldHandleTouches.map { shouldHandle -> !shouldHandle }
+
override val isAod =
displayState.map { it == AuthenticateOptions.DISPLAY_STATE_AOD }.distinctUntilChanged()
@@ -159,6 +164,12 @@
.catch { t -> Log.w(TAG, "failed to notify new display state", t) }
.launchIn(this)
+ isHardwareIgnoringTouches
+ .distinctUntilChanged()
+ .onEach { state -> listener.onHardwareIgnoreTouchesChanged(state) }
+ .catch { t -> Log.w(TAG, "failed to notify new set ignore state", t) }
+ .launchIn(this)
+
listener.asBinder().linkToDeath({ cancel() }, 0)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractor.kt b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractor.kt
index f4a2811..4fc1b58 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractor.kt
@@ -30,8 +30,10 @@
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
@@ -56,6 +58,16 @@
return isUdfpsEnrolled && isWithinOverlayBounds
}
+ /** Sets whether Udfps overlay should handle touches */
+ fun setHandleTouches(shouldHandle: Boolean = true) {
+ _shouldHandleTouches.value = shouldHandle
+ }
+
+ private var _shouldHandleTouches = MutableStateFlow(true)
+
+ /** Whether Udfps overlay should handle touches */
+ val shouldHandleTouches: StateFlow<Boolean> = _shouldHandleTouches.asStateFlow()
+
/** Returns the current udfpsOverlayParams */
val udfpsOverlayParams: StateFlow<UdfpsOverlayParams> =
ConflatedCallbackFlow.conflatedCallbackFlow {
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt
index 7a96fab..09c18ed 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt
@@ -23,7 +23,9 @@
import com.android.systemui.communal.widgets.WidgetInteractionHandler
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.media.controls.ui.MediaHierarchyManager
import com.android.systemui.media.controls.ui.MediaHost
+import com.android.systemui.media.controls.ui.MediaHostState
import com.android.systemui.media.dagger.MediaModule
import javax.inject.Inject
import javax.inject.Named
@@ -70,6 +72,17 @@
override val isPopupOnDismissCtaShowing: Flow<Boolean> =
_isPopupOnDismissCtaShowing.asStateFlow()
+ init {
+ // Initialize our media host for the UMO. This only needs to happen once and must be done
+ // before the MediaHierarchyManager attempts to move the UMO to the hub.
+ with(mediaHost) {
+ expansion = MediaHostState.EXPANDED
+ showsOnlyActiveMedia = false
+ falsingProtectionNeeded = false
+ init(MediaHierarchyManager.LOCATION_COMMUNAL_HUB)
+ }
+ }
+
override fun onOpenWidgetEditor() = communalInteractor.showWidgetEditor()
override fun onDismissCtaTile() {
diff --git a/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlags.kt b/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlags.kt
index 8c3e4a5..ab08f66 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlags.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlags.kt
@@ -44,6 +44,7 @@
keyguardBottomAreaRefactor() &&
KeyguardShadeMigrationNssl.isEnabled &&
MediaInSceneContainerFlag.isEnabled &&
+ // NOTE: Changes should also be made in getSecondaryFlags and @EnableSceneContainer
ComposeFacade.isComposeAvailable()
/**
@@ -63,6 +64,7 @@
FlagToken(FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR, keyguardBottomAreaRefactor()),
KeyguardShadeMigrationNssl.token,
MediaInSceneContainerFlag.token,
+ // NOTE: Changes should also be made in isEnabled and @EnableSceneContainer
)
/** The full set of requirements for SceneContainer */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationStatsLoggerModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationStatsLoggerModule.kt
new file mode 100644
index 0000000..cbd9887
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationStatsLoggerModule.kt
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.dagger
+
+import com.android.systemui.CoreStartable
+import com.android.systemui.NoOpCoreStartable
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.UiBackground
+import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.scene.domain.interactor.WindowRootViewVisibilityInteractor
+import com.android.systemui.statusbar.NotificationListener
+import com.android.systemui.statusbar.notification.collection.NotifLiveDataStore
+import com.android.systemui.statusbar.notification.collection.NotifPipeline
+import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider
+import com.android.systemui.statusbar.notification.logging.NotificationLogger
+import com.android.systemui.statusbar.notification.logging.NotificationLogger.ExpansionStateLogger
+import com.android.systemui.statusbar.notification.logging.NotificationPanelLogger
+import com.android.systemui.statusbar.notification.shared.NotificationsLiveDataStoreRefactor
+import com.android.systemui.statusbar.notification.stack.ui.view.NotificationRowStatsLogger
+import com.android.systemui.statusbar.notification.stack.ui.view.NotificationStatsLogger
+import com.android.systemui.statusbar.notification.stack.ui.view.NotificationStatsLoggerImpl
+import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationLoggerViewModel
+import com.android.systemui.util.kotlin.JavaAdapter
+import com.android.systemui.util.kotlin.getOrNull
+import dagger.Binds
+import dagger.Module
+import dagger.Provides
+import dagger.multibindings.ClassKey
+import dagger.multibindings.IntoMap
+import java.util.Optional
+import java.util.concurrent.Executor
+import javax.inject.Provider
+
+@Module
+interface NotificationStatsLoggerModule {
+
+ /** Binds an implementation to the [NotificationStatsLogger]. */
+ @Binds fun bindsStatsLoggerImpl(impl: NotificationStatsLoggerImpl): NotificationStatsLogger
+
+ companion object {
+
+ /** Provides a [NotificationStatsLogger] if the refactor flag is on. */
+ @Provides
+ fun provideStatsLogger(
+ provider: Provider<NotificationStatsLogger>
+ ): Optional<NotificationStatsLogger> {
+ return if (NotificationsLiveDataStoreRefactor.isEnabled) {
+ Optional.of(provider.get())
+ } else {
+ Optional.empty()
+ }
+ }
+
+ /** Provides a [NotificationLoggerViewModel] if the refactor flag is on. */
+ @Provides
+ fun provideViewModel(
+ provider: Provider<NotificationLoggerViewModel>
+ ): Optional<NotificationLoggerViewModel> {
+ return if (NotificationsLiveDataStoreRefactor.isEnabled) {
+ Optional.of(provider.get())
+ } else {
+ Optional.empty()
+ }
+ }
+
+ /** Provides the legacy [NotificationLogger] if the refactor flag is off. */
+ @Provides
+ @SysUISingleton
+ fun provideLegacyLoggerOptional(
+ notificationListener: NotificationListener?,
+ @UiBackground uiBgExecutor: Executor?,
+ notifLiveDataStore: NotifLiveDataStore?,
+ visibilityProvider: NotificationVisibilityProvider?,
+ notifPipeline: NotifPipeline?,
+ statusBarStateController: StatusBarStateController?,
+ windowRootViewVisibilityInteractor: WindowRootViewVisibilityInteractor?,
+ javaAdapter: JavaAdapter?,
+ expansionStateLogger: ExpansionStateLogger?,
+ notificationPanelLogger: NotificationPanelLogger?
+ ): Optional<NotificationLogger> {
+ return if (NotificationsLiveDataStoreRefactor.isEnabled) {
+ Optional.empty()
+ } else {
+ Optional.of(
+ NotificationLogger(
+ notificationListener,
+ uiBgExecutor,
+ notifLiveDataStore,
+ visibilityProvider,
+ notifPipeline,
+ statusBarStateController,
+ windowRootViewVisibilityInteractor,
+ javaAdapter,
+ expansionStateLogger,
+ notificationPanelLogger
+ )
+ )
+ }
+ }
+
+ /**
+ * Provides a the legacy [NotificationLogger] or the new [NotificationStatsLogger] to the
+ * notification row.
+ *
+ * TODO(b/308623704) remove the [NotificationRowStatsLogger] interface, and provide a
+ * [NotificationStatsLogger] to the row directly.
+ */
+ @Provides
+ fun provideRowStatsLogger(
+ newProvider: Provider<NotificationStatsLogger>,
+ legacyLoggerOptional: Optional<NotificationLogger>,
+ ): NotificationRowStatsLogger {
+ return legacyLoggerOptional.getOrNull() ?: newProvider.get()
+ }
+
+ /**
+ * Binds the legacy [NotificationLogger] as a [CoreStartable] if the feature flag is off, or
+ * binds a no-op [CoreStartable] otherwise.
+ *
+ * The old [NotificationLogger] is a [CoreStartable], because it's managing its own data
+ * updates, but the new [NotificationStatsLogger] is not. Currently Dagger doesn't support
+ * optionally binding entries with @[IntoMap], therefore we provide a no-op [CoreStartable]
+ * here if the feature flag is on, but this can be removed once the flag is released.
+ */
+ @Provides
+ @IntoMap
+ @ClassKey(NotificationLogger::class)
+ fun provideCoreStartable(
+ legacyLoggerOptional: Optional<NotificationLogger>
+ ): CoreStartable {
+ return legacyLoggerOptional.getOrNull() ?: NoOpCoreStartable()
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
index 3a72205..6bba72b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
@@ -17,14 +17,12 @@
package com.android.systemui.statusbar.notification.dagger;
import android.content.Context;
+import android.service.notification.NotificationListenerService;
import com.android.internal.jank.InteractionJankMonitor;
import com.android.systemui.CoreStartable;
import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.dagger.qualifiers.UiBackground;
-import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.res.R;
-import com.android.systemui.scene.domain.interactor.WindowRootViewVisibilityInteractor;
import com.android.systemui.statusbar.NotificationListener;
import com.android.systemui.statusbar.notification.NotificationActivityStarter;
import com.android.systemui.statusbar.notification.NotificationLaunchAnimatorControllerProvider;
@@ -67,7 +65,6 @@
import com.android.systemui.statusbar.notification.interruption.VisualInterruptionDecisionProvider;
import com.android.systemui.statusbar.notification.interruption.VisualInterruptionDecisionProviderImpl;
import com.android.systemui.statusbar.notification.interruption.VisualInterruptionRefactor;
-import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.logging.NotificationPanelLogger;
import com.android.systemui.statusbar.notification.logging.NotificationPanelLoggerImpl;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
@@ -80,7 +77,8 @@
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.StatusBarNotificationActivityStarter;
import com.android.systemui.statusbar.policy.HeadsUpManager;
-import com.android.systemui.util.kotlin.JavaAdapter;
+
+import javax.inject.Provider;
import dagger.Binds;
import dagger.Module;
@@ -88,10 +86,6 @@
import dagger.multibindings.ClassKey;
import dagger.multibindings.IntoMap;
-import java.util.concurrent.Executor;
-
-import javax.inject.Provider;
-
/**
* Dagger Module for classes found within the com.android.systemui.statusbar.notification package.
*/
@@ -104,6 +98,7 @@
NotificationSectionHeadersModule.class,
ActivatableNotificationViewModelModule.class,
NotificationMemoryModule.class,
+ NotificationStatsLoggerModule.class,
})
public interface NotificationsModule {
@Binds
@@ -128,39 +123,6 @@
VisibilityLocationProvider bindVisibilityLocationProvider(
VisibilityLocationProviderDelegator visibilityLocationProviderDelegator);
- /** Provides an instance of {@link NotificationLogger} */
- @SysUISingleton
- @Provides
- static NotificationLogger provideNotificationLogger(
- NotificationListener notificationListener,
- @UiBackground Executor uiBgExecutor,
- NotifLiveDataStore notifLiveDataStore,
- NotificationVisibilityProvider visibilityProvider,
- NotifPipeline notifPipeline,
- StatusBarStateController statusBarStateController,
- WindowRootViewVisibilityInteractor windowRootViewVisibilityInteractor,
- JavaAdapter javaAdapter,
- NotificationLogger.ExpansionStateLogger expansionStateLogger,
- NotificationPanelLogger notificationPanelLogger) {
- return new NotificationLogger(
- notificationListener,
- uiBgExecutor,
- notifLiveDataStore,
- visibilityProvider,
- notifPipeline,
- statusBarStateController,
- windowRootViewVisibilityInteractor,
- javaAdapter,
- expansionStateLogger,
- notificationPanelLogger);
- }
-
- /** Binds {@link NotificationLogger} as a {@link CoreStartable}. */
- @Binds
- @IntoMap
- @ClassKey(NotificationLogger.class)
- CoreStartable bindsNotificationLogger(NotificationLogger notificationLogger);
-
/** Provides an instance of {@link NotificationPanelLogger} */
@SysUISingleton
@Provides
@@ -272,6 +234,10 @@
NotifLiveDataStore bindNotifLiveDataStore(NotifLiveDataStoreImpl notifLiveDataStoreImpl);
/** */
+ @Binds
+ NotificationListenerService bindNotificationListener(NotificationListener notificationListener);
+
+ /** */
@Provides
@SysUISingleton
static VisualInterruptionDecisionProvider provideVisualInterruptionDecisionProvider(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/repository/ActiveNotificationListRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/repository/ActiveNotificationListRepository.kt
index 5ed82cc..5c844bc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/repository/ActiveNotificationListRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/repository/ActiveNotificationListRepository.kt
@@ -55,6 +55,12 @@
* invoking [get].
*/
val renderList: List<Key> = emptyList(),
+
+ /**
+ * Map of notification key to rank, where rank is the 0-based index of the notification on the
+ * system server, meaning that in the unfiltered flattened list of notification entries.
+ */
+ val rankingsMap: Map<String, Int> = emptyMap()
) {
operator fun get(key: Key): ActiveNotificationEntryModel? {
return when (key) {
@@ -74,8 +80,9 @@
private val groups = mutableMapOf<String, ActiveNotificationGroupModel>()
private val individuals = mutableMapOf<String, ActiveNotificationModel>()
private val renderList = mutableListOf<Key>()
+ private var rankingsMap: Map<String, Int> = emptyMap()
- fun build() = ActiveNotificationsStore(groups, individuals, renderList)
+ fun build() = ActiveNotificationsStore(groups, individuals, renderList, rankingsMap)
fun addEntry(entry: ActiveNotificationEntryModel) {
when (entry) {
@@ -95,5 +102,9 @@
individuals[group.summary.key] = group.summary
group.children.forEach { individuals[it.key] = it }
}
+
+ fun setRankingsMap(map: Map<String, Int>) {
+ rankingsMap = map.toMap()
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractor.kt
index e90ddf9..ca6344d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractor.kt
@@ -33,7 +33,10 @@
private val repository: ActiveNotificationListRepository,
@Background private val backgroundDispatcher: CoroutineDispatcher,
) {
- /** Notifications actively presented to the user in the notification stack, in order. */
+ /**
+ * Top level list of Notifications actively presented to the user in the notification stack, in
+ * order.
+ */
val topLevelRepresentativeNotifications: Flow<List<ActiveNotificationModel>> =
repository.activeNotifications
.map { store ->
@@ -51,6 +54,13 @@
}
.flowOn(backgroundDispatcher)
+ /**
+ * Flattened list of Notifications actively presented to the user in the notification stack, in
+ * order.
+ */
+ val allRepresentativeNotifications: Flow<Map<String, ActiveNotificationModel>> =
+ repository.activeNotifications.map { store -> store.individuals }
+
/** Are any notifications being actively presented in the notification stack? */
val areAnyNotificationsPresent: Flow<Boolean> =
repository.activeNotifications
@@ -65,6 +75,16 @@
val areAnyNotificationsPresentValue: Boolean
get() = repository.activeNotifications.value.renderList.isNotEmpty()
+ /**
+ * Map of notification key to rank, where rank is the 0-based index of the notification in the
+ * system server, meaning that in the unfiltered flattened list of notification entries. Used
+ * for logging purposes.
+ *
+ * @see [com.android.systemui.statusbar.notification.stack.ui.view.NotificationStatsLogger].
+ */
+ val activeNotificationRanks: Flow<Map<String, Int>> =
+ repository.activeNotifications.map { store -> store.rankingsMap }
+
/** Are there are any notifications that can be cleared by the "Clear all" button? */
val hasClearableNotifications: Flow<Boolean> =
repository.notifStats
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationListInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationListInteractor.kt
index 6f4ed9d..695f215 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationListInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationListInteractor.kt
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.notification.domain.interactor
import android.graphics.drawable.Icon
+import android.util.ArrayMap
import com.android.systemui.statusbar.notification.collection.GroupEntry
import com.android.systemui.statusbar.notification.collection.ListEntry
import com.android.systemui.statusbar.notification.collection.NotificationEntry
@@ -46,6 +47,7 @@
repository.activeNotifications.update { existingModels ->
buildActiveNotificationsStore(existingModels, sectionStyleProvider) {
entries.forEach(::addListEntry)
+ setRankingsMap(entries)
}
}
}
@@ -94,6 +96,27 @@
}
}
+ fun setRankingsMap(entries: List<ListEntry>) {
+ builder.setRankingsMap(flatMapToRankingsMap(entries))
+ }
+
+ fun flatMapToRankingsMap(entries: List<ListEntry>): Map<String, Int> {
+ val result = ArrayMap<String, Int>()
+ for (entry in entries) {
+ if (entry is NotificationEntry) {
+ entry.representativeEntry?.let { representativeEntry ->
+ result[representativeEntry.key] = representativeEntry.ranking.rank
+ }
+ } else if (entry is GroupEntry) {
+ entry.summary?.let { summary -> result[summary.key] = summary.ranking.rank }
+ for (child in entry.children) {
+ result[child.key] = child.ranking.rank
+ }
+ }
+ }
+ return result
+ }
+
private fun NotificationEntry.toModel(): ActiveNotificationModel =
existingModels.createOrReuse(
key = key,
@@ -107,6 +130,11 @@
aodIcon = icons.aodIcon?.sourceIcon,
shelfIcon = icons.shelfIcon?.sourceIcon,
statusBarIcon = icons.statusBarIcon?.sourceIcon,
+ uid = sbn.uid,
+ packageName = sbn.packageName,
+ instanceId = sbn.instanceId?.id,
+ isGroupSummary = sbn.notification.isGroupSummary,
+ bucket = bucket,
)
}
@@ -121,7 +149,12 @@
isPulsing: Boolean,
aodIcon: Icon?,
shelfIcon: Icon?,
- statusBarIcon: Icon?
+ statusBarIcon: Icon?,
+ uid: Int,
+ packageName: String,
+ instanceId: Int?,
+ isGroupSummary: Boolean,
+ bucket: Int,
): ActiveNotificationModel {
return individuals[key]?.takeIf {
it.isCurrent(
@@ -135,7 +168,12 @@
isPulsing = isPulsing,
aodIcon = aodIcon,
shelfIcon = shelfIcon,
- statusBarIcon = statusBarIcon
+ statusBarIcon = statusBarIcon,
+ uid = uid,
+ instanceId = instanceId,
+ isGroupSummary = isGroupSummary,
+ packageName = packageName,
+ bucket = bucket,
)
}
?: ActiveNotificationModel(
@@ -150,6 +188,11 @@
aodIcon = aodIcon,
shelfIcon = shelfIcon,
statusBarIcon = statusBarIcon,
+ uid = uid,
+ instanceId = instanceId,
+ isGroupSummary = isGroupSummary,
+ packageName = packageName,
+ bucket = bucket,
)
}
@@ -164,7 +207,12 @@
isPulsing: Boolean,
aodIcon: Icon?,
shelfIcon: Icon?,
- statusBarIcon: Icon?
+ statusBarIcon: Icon?,
+ uid: Int,
+ packageName: String,
+ instanceId: Int?,
+ isGroupSummary: Boolean,
+ bucket: Int,
): Boolean {
return when {
key != this.key -> false
@@ -178,6 +226,11 @@
aodIcon != this.aodIcon -> false
shelfIcon != this.shelfIcon -> false
statusBarIcon != this.statusBarIcon -> false
+ uid != this.uid -> false
+ instanceId != this.instanceId -> false
+ isGroupSummary != this.isGroupSummary -> false
+ packageName != this.packageName -> false
+ bucket != this.bucket -> false
else -> true
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt
index 7d1cca8..ac49960 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt
@@ -39,6 +39,7 @@
import com.android.systemui.statusbar.notification.interruption.HeadsUpViewBinder
import com.android.systemui.statusbar.notification.logging.NotificationLogger
import com.android.systemui.statusbar.notification.row.NotifBindPipelineInitializer
+import com.android.systemui.statusbar.notification.shared.NotificationsLiveDataStoreRefactor
import com.android.systemui.statusbar.notification.stack.NotificationListContainer
import com.android.wm.shell.bubbles.Bubbles
import dagger.Lazy
@@ -53,7 +54,9 @@
* any initialization work that notifications require.
*/
@SysUISingleton
-class NotificationsControllerImpl @Inject constructor(
+class NotificationsControllerImpl
+@Inject
+constructor(
private val notificationListener: NotificationListener,
private val commonNotifCollection: Lazy<CommonNotifCollection>,
private val notifPipeline: Lazy<NotifPipeline>,
@@ -61,7 +64,7 @@
private val targetSdkResolver: TargetSdkResolver,
private val notifPipelineInitializer: Lazy<NotifPipelineInitializer>,
private val notifBindPipelineInitializer: NotifBindPipelineInitializer,
- private val notificationLogger: NotificationLogger,
+ private val notificationLoggerOptional: Optional<NotificationLogger>,
private val notificationRowBinder: NotificationRowBinderImpl,
private val notificationsMediaManager: NotificationMediaManager,
private val headsUpViewBinder: HeadsUpViewBinder,
@@ -80,28 +83,35 @@
) {
notificationListener.registerAsSystemService()
- notifPipeline.get().addCollectionListener(object : NotifCollectionListener {
- override fun onEntryRemoved(entry: NotificationEntry, reason: Int) {
- listContainer.cleanUpViewStateForEntry(entry)
- }
- })
+ notifPipeline
+ .get()
+ .addCollectionListener(
+ object : NotifCollectionListener {
+ override fun onEntryRemoved(entry: NotificationEntry, reason: Int) {
+ listContainer.cleanUpViewStateForEntry(entry)
+ }
+ }
+ )
notificationRowBinder.setNotificationClicker(
- clickerBuilder.build(bubblesOptional, notificationActivityStarter))
+ clickerBuilder.build(bubblesOptional, notificationActivityStarter)
+ )
notificationRowBinder.setUpWithPresenter(presenter, listContainer)
headsUpViewBinder.setPresenter(presenter)
notifBindPipelineInitializer.initialize()
animatedImageNotificationManager.bind()
- notifPipelineInitializer.get().initialize(
- notificationListener,
- notificationRowBinder,
- listContainer,
- stackController)
+ notifPipelineInitializer
+ .get()
+ .initialize(notificationListener, notificationRowBinder, listContainer, stackController)
targetSdkResolver.initialize(notifPipeline.get())
notificationsMediaManager.setUpWithPresenter(presenter)
- notificationLogger.setUpWithContainer(listContainer)
+ if (!NotificationsLiveDataStoreRefactor.isEnabled) {
+ notificationLoggerOptional.ifPresent { logger ->
+ logger.setUpWithContainer(listContainer)
+ }
+ }
peopleSpaceWidgetManager.attach(notificationListener)
}
@@ -120,11 +130,11 @@
notificationListener.snoozeNotification(sbn.key, snoozeOption.snoozeCriterion.id)
} else {
notificationListener.snoozeNotification(
- sbn.key,
- snoozeOption.minutesToSnoozeFor * 60 * 1000.toLong())
+ sbn.key,
+ snoozeOption.minutesToSnoozeFor * 60 * 1000.toLong()
+ )
}
}
- override fun getActiveNotificationsCount(): Int =
- notifLiveDataStore.activeNotifCount.value
+ override fun getActiveNotificationsCount(): Int = notifLiveDataStore.activeNotifCount.value
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
index 8d2a63e..4349b3b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
@@ -33,6 +33,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.statusbar.NotificationVisibility;
+import com.android.internal.statusbar.NotificationVisibility.NotificationLocation;
import com.android.systemui.CoreStartable;
import com.android.systemui.dagger.qualifiers.UiBackground;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -46,8 +47,10 @@
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
import com.android.systemui.statusbar.notification.dagger.NotificationsModule;
+import com.android.systemui.statusbar.notification.shared.NotificationsLiveDataStoreRefactor;
import com.android.systemui.statusbar.notification.stack.ExpandableViewState;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
+import com.android.systemui.statusbar.notification.stack.ui.view.NotificationRowStatsLogger;
import com.android.systemui.util.Compile;
import com.android.systemui.util.kotlin.JavaAdapter;
@@ -64,7 +67,8 @@
* Handles notification logging, in particular, logging which notifications are visible and which
* are not.
*/
-public class NotificationLogger implements StateListener, CoreStartable {
+public class NotificationLogger implements StateListener, CoreStartable,
+ NotificationRowStatsLogger {
static final String TAG = "NotificationLogger";
private static final boolean DEBUG = Compile.IS_DEBUG && Log.isLoggable(TAG, Log.DEBUG);
@@ -166,31 +170,31 @@
/**
* Returns the location of the notification referenced by the given {@link NotificationEntry}.
*/
- public static NotificationVisibility.NotificationLocation getNotificationLocation(
+ public static NotificationLocation getNotificationLocation(
NotificationEntry entry) {
if (entry == null || entry.getRow() == null || entry.getRow().getViewState() == null) {
- return NotificationVisibility.NotificationLocation.LOCATION_UNKNOWN;
+ return NotificationLocation.LOCATION_UNKNOWN;
}
return convertNotificationLocation(entry.getRow().getViewState().location);
}
- private static NotificationVisibility.NotificationLocation convertNotificationLocation(
+ private static NotificationLocation convertNotificationLocation(
int location) {
switch (location) {
case ExpandableViewState.LOCATION_FIRST_HUN:
- return NotificationVisibility.NotificationLocation.LOCATION_FIRST_HEADS_UP;
+ return NotificationLocation.LOCATION_FIRST_HEADS_UP;
case ExpandableViewState.LOCATION_HIDDEN_TOP:
- return NotificationVisibility.NotificationLocation.LOCATION_HIDDEN_TOP;
+ return NotificationLocation.LOCATION_HIDDEN_TOP;
case ExpandableViewState.LOCATION_MAIN_AREA:
- return NotificationVisibility.NotificationLocation.LOCATION_MAIN_AREA;
+ return NotificationLocation.LOCATION_MAIN_AREA;
case ExpandableViewState.LOCATION_BOTTOM_STACK_PEEKING:
- return NotificationVisibility.NotificationLocation.LOCATION_BOTTOM_STACK_PEEKING;
+ return NotificationLocation.LOCATION_BOTTOM_STACK_PEEKING;
case ExpandableViewState.LOCATION_BOTTOM_STACK_HIDDEN:
- return NotificationVisibility.NotificationLocation.LOCATION_BOTTOM_STACK_HIDDEN;
+ return NotificationLocation.LOCATION_BOTTOM_STACK_HIDDEN;
case ExpandableViewState.LOCATION_GONE:
- return NotificationVisibility.NotificationLocation.LOCATION_GONE;
+ return NotificationLocation.LOCATION_GONE;
default:
- return NotificationVisibility.NotificationLocation.LOCATION_UNKNOWN;
+ return NotificationLocation.LOCATION_UNKNOWN;
}
}
@@ -207,6 +211,9 @@
JavaAdapter javaAdapter,
ExpansionStateLogger expansionStateLogger,
NotificationPanelLogger notificationPanelLogger) {
+ // Not expected to be constructed if the feature flag is on
+ NotificationsLiveDataStoreRefactor.assertInLegacyMode();
+
mNotificationListener = notificationListener;
mUiBgExecutor = uiBgExecutor;
mNotifLiveDataStore = notifLiveDataStore;
@@ -382,9 +389,11 @@
/**
* Called when the notification is expanded / collapsed.
*/
- public void onExpansionChanged(String key, boolean isUserAction, boolean isExpanded) {
- NotificationVisibility.NotificationLocation location = mVisibilityProvider.getLocation(key);
- mExpansionStateLogger.onExpansionChanged(key, isUserAction, isExpanded, location);
+ @Override
+ public void onNotificationExpansionChanged(@NonNull String key, boolean isExpanded,
+ int location, boolean isUserAction) {
+ NotificationLocation notifLocation = mVisibilityProvider.getLocation(key);
+ mExpansionStateLogger.onExpansionChanged(key, isUserAction, isExpanded, notifLocation);
}
@VisibleForTesting
@@ -440,7 +449,7 @@
@VisibleForTesting
void onExpansionChanged(String key, boolean isUserAction, boolean isExpanded,
- NotificationVisibility.NotificationLocation location) {
+ NotificationLocation location) {
State state = getState(key);
state.mIsUserAction = isUserAction;
state.mIsExpanded = isExpanded;
@@ -528,7 +537,7 @@
@Nullable
Boolean mIsVisible;
@Nullable
- NotificationVisibility.NotificationLocation mLocation;
+ NotificationLocation mLocation;
private State() {}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationPanelLogger.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationPanelLogger.java
index 5ca13c9..6c63d1d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationPanelLogger.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationPanelLogger.java
@@ -40,6 +40,11 @@
/**
* Log a NOTIFICATION_PANEL_REPORTED statsd event.
+ */
+ void logPanelShown(boolean isLockscreen, Notifications.NotificationList proto);
+
+ /**
+ * Log a NOTIFICATION_PANEL_REPORTED statsd event.
* @param visibleNotifications as provided by NotificationEntryManager.getVisibleNotifications()
*/
void logPanelShown(boolean isLockscreen,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationPanelLoggerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationPanelLoggerImpl.java
index 9a63228..d7f7b76 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationPanelLoggerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationPanelLoggerImpl.java
@@ -21,6 +21,7 @@
import com.android.systemui.shared.system.SysUiStatsLog;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.logging.nano.Notifications;
+import com.android.systemui.statusbar.notification.shared.NotificationsLiveDataStoreRefactor;
import com.google.protobuf.nano.MessageNano;
@@ -31,15 +32,25 @@
* Normal implementation of NotificationPanelLogger.
*/
public class NotificationPanelLoggerImpl implements NotificationPanelLogger {
+
+ @Override
+ public void logPanelShown(boolean isLockscreen, Notifications.NotificationList proto) {
+ SysUiStatsLog.write(SysUiStatsLog.NOTIFICATION_PANEL_REPORTED,
+ /* event_id = */ NotificationPanelEvent.fromLockscreen(isLockscreen).getId(),
+ /* num_notifications = */ proto.notifications.length,
+ /* notifications = */ MessageNano.toByteArray(proto));
+ }
+
@Override
public void logPanelShown(boolean isLockscreen,
List<NotificationEntry> visibleNotifications) {
+ NotificationsLiveDataStoreRefactor.assertInLegacyMode();
final Notifications.NotificationList proto = NotificationPanelLogger.toNotificationProto(
visibleNotifications);
SysUiStatsLog.write(SysUiStatsLog.NOTIFICATION_PANEL_REPORTED,
- /* int event_id */ NotificationPanelEvent.fromLockscreen(isLockscreen).getId(),
- /* int num_notifications*/ proto.notifications.length,
- /* byte[] notifications*/ MessageNano.toByteArray(proto));
+ /* event_id = */ NotificationPanelEvent.fromLockscreen(isLockscreen).getId(),
+ /* num_notifications = */ proto.notifications.length,
+ /* notifications = */ MessageNano.toByteArray(proto));
}
@Override
@@ -47,8 +58,8 @@
final Notifications.NotificationList proto = NotificationPanelLogger.toNotificationProto(
Collections.singletonList(draggedNotification));
SysUiStatsLog.write(SysUiStatsLog.NOTIFICATION_PANEL_REPORTED,
- /* int event_id */ NOTIFICATION_DRAG.getId(),
- /* int num_notifications*/ proto.notifications.length,
- /* byte[] notifications*/ MessageNano.toByteArray(proto));
+ /* event_id = */ NOTIFICATION_DRAG.getId(),
+ /* num_notifications = */ proto.notifications.length,
+ /* notifications = */ MessageNano.toByteArray(proto));
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index e200e65..5eeb066 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -118,6 +118,7 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
+import java.util.Map;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import java.util.function.BooleanSupplier;
@@ -988,6 +989,25 @@
}
/**
+ * Recursively collects the [{@link ExpandableViewState#location}]s populating the provided
+ * map.
+ * The visibility of each child is determined by the {@link View#getVisibility()}.
+ * Locations are added to the provided map including locations from child views, that are
+ * visible.
+ */
+ public void collectVisibleLocations(Map<String, Integer> locationsMap) {
+ if (getVisibility() == View.VISIBLE) {
+ locationsMap.put(getEntry().getKey(), getViewState().location);
+ if (mChildrenContainer != null) {
+ List<ExpandableNotificationRow> children = mChildrenContainer.getAttachedChildren();
+ for (int i = 0; i < children.size(); i++) {
+ children.get(i).collectVisibleLocations(locationsMap);
+ }
+ }
+ }
+ }
+
+ /**
* Updates states of all children.
*/
public void updateChildrenStates() {
@@ -1615,7 +1635,8 @@
/**
* Called when the notification is expanded / collapsed.
*/
- void logNotificationExpansion(String key, boolean userAction, boolean expanded);
+ void logNotificationExpansion(String key, int location, boolean userAction,
+ boolean expanded);
/**
* Called when a notification which was previously kept in its parent for the
@@ -3312,7 +3333,8 @@
if (nowExpanded != wasExpanded) {
updateShelfIconColor();
if (mLogger != null) {
- mLogger.logNotificationExpansion(mLoggingKey, userAction, nowExpanded);
+ mLogger.logNotificationExpansion(mLoggingKey, getViewState().location, userAction,
+ nowExpanded);
}
if (mIsSummaryWithChildren) {
mChildrenContainer.onExpansionChanged();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
index af55f44..0afdefa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
@@ -48,13 +48,13 @@
import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
import com.android.systemui.statusbar.notification.collection.render.NodeController;
import com.android.systemui.statusbar.notification.collection.render.NotifViewController;
-import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
import com.android.systemui.statusbar.notification.row.dagger.AppName;
import com.android.systemui.statusbar.notification.row.dagger.NotificationKey;
import com.android.systemui.statusbar.notification.row.dagger.NotificationRowScope;
import com.android.systemui.statusbar.notification.stack.NotificationChildrenContainerLogger;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
+import com.android.systemui.statusbar.notification.stack.ui.view.NotificationRowStatsLogger;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.SmartReplyConstants;
@@ -90,7 +90,7 @@
private final GroupMembershipManager mGroupMembershipManager;
private final GroupExpansionManager mGroupExpansionManager;
private final RowContentBindStage mRowContentBindStage;
- private final NotificationLogger mNotificationLogger;
+ private final NotificationRowStatsLogger mStatsLogger;
private final NotificationRowLogger mLogBufferLogger;
private final HeadsUpManager mHeadsUpManager;
private final ExpandableNotificationRow.OnExpandClickListener mOnExpandClickListener;
@@ -130,9 +130,10 @@
private final ExpandableNotificationRow.ExpandableNotificationRowLogger mLoggerCallback =
new ExpandableNotificationRow.ExpandableNotificationRowLogger() {
@Override
- public void logNotificationExpansion(String key, boolean userAction,
+ public void logNotificationExpansion(String key, int location, boolean userAction,
boolean expanded) {
- mNotificationLogger.onExpansionChanged(key, userAction, expanded);
+ mStatsLogger.onNotificationExpansionChanged(key, expanded, location,
+ userAction);
}
@Override
@@ -212,7 +213,7 @@
GroupMembershipManager groupMembershipManager,
GroupExpansionManager groupExpansionManager,
RowContentBindStage rowContentBindStage,
- NotificationLogger notificationLogger,
+ NotificationRowStatsLogger statsLogger,
HeadsUpManager headsUpManager,
ExpandableNotificationRow.OnExpandClickListener onExpandClickListener,
StatusBarStateController statusBarStateController,
@@ -239,7 +240,7 @@
mGroupMembershipManager = groupMembershipManager;
mGroupExpansionManager = groupExpansionManager;
mRowContentBindStage = rowContentBindStage;
- mNotificationLogger = notificationLogger;
+ mStatsLogger = statsLogger;
mHeadsUpManager = headsUpManager;
mOnExpandClickListener = onExpandClickListener;
mStatusBarStateController = statusBarStateController;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/ActiveNotificationModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/ActiveNotificationModel.kt
index eb1c1ba..5527efc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/ActiveNotificationModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/ActiveNotificationModel.kt
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.notification.shared
import android.graphics.drawable.Icon
+import com.android.systemui.statusbar.notification.stack.PriorityBucket
/**
* Model for a top-level "entry" in the notification list, either an
@@ -55,6 +56,16 @@
val shelfIcon: Icon?,
/** Icon to display in the status bar. */
val statusBarIcon: Icon?,
+ /** The notifying app's [packageName]'s uid. */
+ val uid: Int,
+ /** The notifying app's packageName. */
+ val packageName: String,
+ /** A small per-notification ID, used for statsd logging. */
+ val instanceId: Int?,
+ /** If this notification is the group summary for a group of notifications. */
+ val isGroupSummary: Boolean,
+ /** Indicates in which section the notification is displayed in. @see [PriorityBucket]. */
+ @PriorityBucket val bucket: Int,
) : ActiveNotificationEntryModel()
/** Model for a group of notifications. */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationListContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationListContainer.java
index 6bb9573..5c9a0b9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationListContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationListContainer.java
@@ -23,7 +23,6 @@
import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
import com.android.systemui.statusbar.notification.LaunchAnimationParameters;
-import com.android.systemui.statusbar.notification.NotificationActivityStarter;
import com.android.systemui.statusbar.notification.VisibilityLocationProvider;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index ea414d2..e791a64 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -113,6 +113,7 @@
import com.android.systemui.statusbar.notification.row.ExpandableView;
import com.android.systemui.statusbar.notification.row.StackScrollerDecorView;
import com.android.systemui.statusbar.notification.shared.NotificationsImprovedHunAnimation;
+import com.android.systemui.statusbar.notification.shared.NotificationsLiveDataStoreRefactor;
import com.android.systemui.statusbar.phone.HeadsUpAppearanceController;
import com.android.systemui.statusbar.phone.HeadsUpTouchHelper;
import com.android.systemui.statusbar.phone.ScreenOffAnimationController;
@@ -129,9 +130,12 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
+import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
+import java.util.Map;
import java.util.Set;
+import java.util.concurrent.Callable;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
@@ -245,6 +249,7 @@
*/
private float mOverScrolledBottomPixels;
private NotificationLogger.OnChildLocationsChangedListener mListener;
+ private OnNotificationLocationsChangedListener mLocationsChangedListener;
private OnOverscrollTopChangedListener mOverscrollTopChangedListener;
private ExpandableView.OnHeightChangedListener mOnHeightChangedListener;
private Runnable mOnHeightChangedRunnable;
@@ -390,6 +395,14 @@
}
};
+ private final Callable<Map<String, Integer>> collectVisibleLocationsCallable =
+ new Callable<>() {
+ @Override
+ public Map<String, Integer> call() {
+ return collectVisibleNotificationLocations();
+ }
+ };
+
private boolean mPulsing;
private boolean mScrollable;
private View mForcedScroll;
@@ -1242,8 +1255,21 @@
}
}
+ /**
+ * @param listener to be notified after the location of Notification children might have
+ * changed.
+ */
+ public void setNotificationLocationsChangedListener(
+ @Nullable OnNotificationLocationsChangedListener listener) {
+ if (NotificationsLiveDataStoreRefactor.isUnexpectedlyInLegacyMode()) {
+ return;
+ }
+ mLocationsChangedListener = listener;
+ }
+
public void setChildLocationsChangedListener(
NotificationLogger.OnChildLocationsChangedListener listener) {
+ NotificationsLiveDataStoreRefactor.assertInLegacyMode();
mListener = listener;
}
@@ -4398,15 +4424,40 @@
child.applyViewState();
}
- if (mListener != null) {
- mListener.onChildLocationsChanged();
+ if (NotificationsLiveDataStoreRefactor.isEnabled()) {
+ if (mLocationsChangedListener != null) {
+ mLocationsChangedListener.onChildLocationsChanged(collectVisibleLocationsCallable);
+ }
+ } else {
+ if (mListener != null) {
+ mListener.onChildLocationsChanged();
+ }
}
+
runAnimationFinishedRunnables();
setAnimationRunning(false);
updateBackground();
updateViewShadows();
}
+ /**
+ * Retrieves a map of visible [{@link ExpandableViewState#location}]s of the actively displayed
+ * Notification children associated by their Notification keys.
+ * Locations are collected recursively including locations from the child views of Notification
+ * Groups, that are visible.
+ */
+ private Map<String, Integer> collectVisibleNotificationLocations() {
+ Map<String, Integer> visibilities = new HashMap<>();
+ int numChildren = getChildCount();
+ for (int i = 0; i < numChildren; i++) {
+ ExpandableView child = getChildAtIndex(i);
+ if (child instanceof ExpandableNotificationRow row) {
+ row.collectVisibleLocations(visibilities);
+ }
+ }
+ return visibilities;
+ }
+
private void updateViewShadows() {
// we need to work around an issue where the shadow would not cast between siblings when
// their z difference is between 0 and 0.1
@@ -5901,7 +5952,11 @@
}
}
- protected void setLogger(StackStateLogger logger) {
+ /**
+ * Sets a {@link StackStateLogger} which is notified as the {@link StackStateAnimator} updates
+ * the views.
+ */
+ protected void setStackStateLogger(StackStateLogger logger) {
mStateAnimator.setLogger(logger);
}
@@ -5938,6 +5993,18 @@
void flingTopOverscroll(float velocity, boolean open);
}
+ /**
+ * A listener that is notified when some ExpandableNotificationRow locations might have changed.
+ */
+ public interface OnNotificationLocationsChangedListener {
+ /**
+ * Called when the location of ExpandableNotificationRows might have changed.
+ *
+ * @param locations mapping of Notification keys to locations.
+ */
+ void onChildLocationsChanged(Callable<Map<String, Integer>> locations);
+ }
+
private void updateSpeedBumpIndex() {
mSpeedBumpIndexDirty = true;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index a30c294..c0e0b73 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -751,7 +751,7 @@
}
private void setUpView() {
- mView.setLogger(mStackStateLogger);
+ mView.setStackStateLogger(mStackStateLogger);
mView.setController(this);
mView.setLogger(mLogger);
mView.setTouchHandler(new TouchHandler());
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/shared/model/SceneContainerConfigKosmos.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/NotificationRowStatsLogger.kt
similarity index 70%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/scene/shared/model/SceneContainerConfigKosmos.kt
copy to packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/NotificationRowStatsLogger.kt
index f9cdc1b..2305c7e 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/shared/model/SceneContainerConfigKosmos.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/NotificationRowStatsLogger.kt
@@ -14,9 +14,13 @@
* limitations under the License.
*/
-package com.android.systemui.scene.shared.model
+package com.android.systemui.statusbar.notification.stack.ui.view
-import com.android.systemui.kosmos.Kosmos
-
-val Kosmos.sceneContainerConfig by
- Kosmos.Fixture { FakeSceneContainerConfigModule().sceneContainerConfig }
+interface NotificationRowStatsLogger {
+ fun onNotificationExpansionChanged(
+ key: String,
+ isExpanded: Boolean,
+ location: Int,
+ isUserAction: Boolean
+ )
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/NotificationStatsLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/NotificationStatsLogger.kt
new file mode 100644
index 0000000..5418616
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/NotificationStatsLogger.kt
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.stack.ui.view
+
+import com.android.systemui.statusbar.notification.shared.ActiveNotificationModel
+import java.util.concurrent.Callable
+
+/**
+ * Logs UI events of Notifications, in particular, logging which Notifications are visible and which
+ * are not.
+ */
+interface NotificationStatsLogger : NotificationRowStatsLogger {
+ fun onLockscreenOrShadeInteractive(
+ isOnLockScreen: Boolean,
+ activeNotifications: List<ActiveNotificationModel>,
+ )
+ fun onLockscreenOrShadeNotInteractive(activeNotifications: List<ActiveNotificationModel>)
+ fun onNotificationRemoved(key: String)
+ fun onNotificationUpdated(key: String)
+ fun onNotificationListUpdated(
+ locationsProvider: Callable<Map<String, Int>>,
+ notificationRanks: Map<String, Int>,
+ )
+ override fun onNotificationExpansionChanged(
+ key: String,
+ isExpanded: Boolean,
+ location: Int,
+ isUserAction: Boolean
+ )
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/NotificationStatsLoggerImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/NotificationStatsLoggerImpl.kt
new file mode 100644
index 0000000..0cb00bc
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/NotificationStatsLoggerImpl.kt
@@ -0,0 +1,326 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.stack.ui.view
+
+import android.service.notification.NotificationListenerService
+import androidx.annotation.VisibleForTesting
+import com.android.internal.statusbar.IStatusBarService
+import com.android.internal.statusbar.NotificationVisibility
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.statusbar.notification.logging.NotificationPanelLogger
+import com.android.systemui.statusbar.notification.logging.nano.Notifications
+import com.android.systemui.statusbar.notification.shared.ActiveNotificationModel
+import com.android.systemui.statusbar.notification.stack.ExpandableViewState
+import java.util.concurrent.Callable
+import java.util.concurrent.ConcurrentHashMap
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
+
+@VisibleForTesting const val UNKNOWN_RANK = -1
+
+@SysUISingleton
+class NotificationStatsLoggerImpl
+@Inject
+constructor(
+ @Application private val applicationScope: CoroutineScope,
+ @Background private val bgDispatcher: CoroutineDispatcher,
+ private val notificationListenerService: NotificationListenerService,
+ private val notificationPanelLogger: NotificationPanelLogger,
+ private val statusBarService: IStatusBarService,
+) : NotificationStatsLogger {
+ private val lastLoggedVisibilities = mutableMapOf<String, VisibilityState>()
+ private var logVisibilitiesJob: Job? = null
+
+ private val expansionStates: MutableMap<String, ExpansionState> =
+ ConcurrentHashMap<String, ExpansionState>()
+ private val lastReportedExpansionValues: MutableMap<String, Boolean> =
+ ConcurrentHashMap<String, Boolean>()
+
+ override fun onNotificationListUpdated(
+ locationsProvider: Callable<Map<String, Int>>,
+ notificationRanks: Map<String, Int>,
+ ) {
+ if (logVisibilitiesJob?.isActive == true) {
+ return
+ }
+
+ logVisibilitiesJob =
+ startLogVisibilitiesJob(
+ newVisibilities =
+ combine(
+ visibilities = locationsProvider.call(),
+ rankingsMap = notificationRanks
+ ),
+ activeNotifCount = notificationRanks.size,
+ )
+ }
+
+ override fun onNotificationExpansionChanged(
+ key: String,
+ isExpanded: Boolean,
+ location: Int,
+ isUserAction: Boolean,
+ ) {
+ val expansionState =
+ ExpansionState(
+ key = key,
+ isExpanded = isExpanded,
+ isUserAction = isUserAction,
+ location = location,
+ )
+ expansionStates[key] = expansionState
+ maybeLogNotificationExpansionChange(expansionState)
+ }
+
+ private fun maybeLogNotificationExpansionChange(expansionState: ExpansionState) {
+ if (expansionState.visible.not()) {
+ // Only log visible expansion changes
+ return
+ }
+
+ val loggedExpansionValue: Boolean? = lastReportedExpansionValues[expansionState.key]
+ if (loggedExpansionValue == null && !expansionState.isExpanded) {
+ // Consider the Notification initially collapsed, so only expanded is logged in the
+ // first time.
+ return
+ }
+
+ if (loggedExpansionValue != null && loggedExpansionValue == expansionState.isExpanded) {
+ // We have already logged this state, don't log it again
+ return
+ }
+
+ logNotificationExpansionChange(expansionState)
+ lastReportedExpansionValues[expansionState.key] = expansionState.isExpanded
+ }
+
+ private fun logNotificationExpansionChange(expansionState: ExpansionState) =
+ applicationScope.launch {
+ withContext(bgDispatcher) {
+ statusBarService.onNotificationExpansionChanged(
+ /* key = */ expansionState.key,
+ /* userAction = */ expansionState.isUserAction,
+ /* expanded = */ expansionState.isExpanded,
+ /* notificationLocation = */ expansionState.location
+ .toNotificationLocation()
+ .ordinal
+ )
+ }
+ }
+
+ override fun onLockscreenOrShadeInteractive(
+ isOnLockScreen: Boolean,
+ activeNotifications: List<ActiveNotificationModel>,
+ ) {
+ applicationScope.launch {
+ withContext(bgDispatcher) {
+ notificationPanelLogger.logPanelShown(
+ isOnLockScreen,
+ activeNotifications.toNotificationProto()
+ )
+ }
+ }
+ }
+
+ override fun onLockscreenOrShadeNotInteractive(
+ activeNotifications: List<ActiveNotificationModel>
+ ) {
+ logVisibilitiesJob =
+ startLogVisibilitiesJob(
+ newVisibilities = emptyMap(),
+ activeNotifCount = activeNotifications.size
+ )
+ }
+
+ // TODO(b/308623704) wire this in with NotifPipeline updates
+ override fun onNotificationRemoved(key: String) {
+ // No need to track expansion states for Notifications that are removed.
+ expansionStates.remove(key)
+ lastReportedExpansionValues.remove(key)
+ }
+
+ // TODO(b/308623704) wire this in with NotifPipeline updates
+ override fun onNotificationUpdated(key: String) {
+ // When the Notification is updated, we should consider it as not yet logged.
+ lastReportedExpansionValues.remove(key)
+ }
+
+ private fun combine(
+ visibilities: Map<String, Int>,
+ rankingsMap: Map<String, Int>
+ ): Map<String, VisibilityState> =
+ visibilities.mapValues { entry ->
+ VisibilityState(entry.key, entry.value, rankingsMap[entry.key] ?: UNKNOWN_RANK)
+ }
+
+ private fun startLogVisibilitiesJob(
+ newVisibilities: Map<String, VisibilityState>,
+ activeNotifCount: Int,
+ ) =
+ applicationScope.launch {
+ val newlyVisible = newVisibilities - lastLoggedVisibilities.keys
+ val noLongerVisible = lastLoggedVisibilities - newVisibilities.keys
+
+ maybeLogVisibilityChanges(newlyVisible, noLongerVisible, activeNotifCount)
+ updateExpansionStates(newlyVisible, noLongerVisible)
+
+ lastLoggedVisibilities.clear()
+ lastLoggedVisibilities.putAll(newVisibilities)
+ }
+
+ private suspend fun maybeLogVisibilityChanges(
+ newlyVisible: Map<String, VisibilityState>,
+ noLongerVisible: Map<String, VisibilityState>,
+ activeNotifCount: Int,
+ ) {
+ if (newlyVisible.isEmpty() && noLongerVisible.isEmpty()) {
+ return
+ }
+
+ val newlyVisibleAr =
+ newlyVisible.mapToNotificationVisibilitiesAr(visible = true, count = activeNotifCount)
+
+ val noLongerVisibleAr =
+ noLongerVisible.mapToNotificationVisibilitiesAr(
+ visible = false,
+ count = activeNotifCount
+ )
+
+ withContext(bgDispatcher) {
+ statusBarService.onNotificationVisibilityChanged(newlyVisibleAr, noLongerVisibleAr)
+ if (newlyVisible.isNotEmpty()) {
+ notificationListenerService.setNotificationsShown(newlyVisible.keys.toTypedArray())
+ }
+ }
+ }
+
+ private fun updateExpansionStates(
+ newlyVisible: Map<String, VisibilityState>,
+ noLongerVisible: Map<String, VisibilityState>
+ ) {
+ expansionStates.forEach { (key, expansionState) ->
+ if (newlyVisible.contains(key)) {
+ val newState =
+ expansionState.copy(
+ visible = true,
+ location = newlyVisible.getValue(key).location,
+ )
+ expansionStates[key] = newState
+ maybeLogNotificationExpansionChange(newState)
+ }
+
+ if (noLongerVisible.contains(key)) {
+ expansionStates[key] =
+ expansionState.copy(
+ visible = false,
+ location = noLongerVisible.getValue(key).location,
+ )
+ }
+ }
+ }
+
+ private data class VisibilityState(
+ val key: String,
+ val location: Int,
+ val rank: Int,
+ )
+
+ private data class ExpansionState(
+ val key: String,
+ val isUserAction: Boolean,
+ val isExpanded: Boolean,
+ val visible: Boolean,
+ val location: Int,
+ ) {
+ constructor(
+ key: String,
+ isExpanded: Boolean,
+ location: Int,
+ isUserAction: Boolean,
+ ) : this(
+ key = key,
+ isExpanded = isExpanded,
+ isUserAction = isUserAction,
+ visible = isVisibleLocation(location),
+ location = location,
+ )
+ }
+
+ private fun Map<String, VisibilityState>.mapToNotificationVisibilitiesAr(
+ visible: Boolean,
+ count: Int,
+ ): Array<NotificationVisibility> =
+ this.map { (key, state) ->
+ NotificationVisibility.obtain(
+ /* key = */ key,
+ /* rank = */ state.rank,
+ /* count = */ count,
+ /* visible = */ visible,
+ /* location = */ state.location.toNotificationLocation()
+ )
+ }
+ .toTypedArray()
+}
+
+private fun Int.toNotificationLocation(): NotificationVisibility.NotificationLocation {
+ return when (this) {
+ ExpandableViewState.LOCATION_FIRST_HUN ->
+ NotificationVisibility.NotificationLocation.LOCATION_FIRST_HEADS_UP
+ ExpandableViewState.LOCATION_HIDDEN_TOP ->
+ NotificationVisibility.NotificationLocation.LOCATION_HIDDEN_TOP
+ ExpandableViewState.LOCATION_MAIN_AREA ->
+ NotificationVisibility.NotificationLocation.LOCATION_MAIN_AREA
+ ExpandableViewState.LOCATION_BOTTOM_STACK_PEEKING ->
+ NotificationVisibility.NotificationLocation.LOCATION_BOTTOM_STACK_PEEKING
+ ExpandableViewState.LOCATION_BOTTOM_STACK_HIDDEN ->
+ NotificationVisibility.NotificationLocation.LOCATION_BOTTOM_STACK_HIDDEN
+ ExpandableViewState.LOCATION_GONE ->
+ NotificationVisibility.NotificationLocation.LOCATION_GONE
+ else -> NotificationVisibility.NotificationLocation.LOCATION_UNKNOWN
+ }
+}
+
+private fun List<ActiveNotificationModel>.toNotificationProto(): Notifications.NotificationList {
+ val notificationList = Notifications.NotificationList()
+ val protoArray: Array<Notifications.Notification> =
+ map { notification ->
+ Notifications.Notification().apply {
+ uid = notification.uid
+ packageName = notification.packageName
+ notification.instanceId?.let { instanceId = it }
+ // TODO(b/308623704) check if we can set groupInstanceId as well
+ isGroupSummary = notification.isGroupSummary
+ section = NotificationPanelLogger.toNotificationSection(notification.bucket)
+ }
+ }
+ .toTypedArray()
+
+ if (protoArray.isNotEmpty()) {
+ notificationList.notifications = protoArray
+ }
+
+ return notificationList
+}
+
+private fun isVisibleLocation(location: Int): Boolean =
+ location and ExpandableViewState.VISIBLE_LOCATIONS != 0
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt
index 1b36660..da260cb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt
@@ -33,13 +33,16 @@
import com.android.systemui.statusbar.notification.footer.ui.view.FooterView
import com.android.systemui.statusbar.notification.footer.ui.viewbinder.FooterViewBinder
import com.android.systemui.statusbar.notification.icon.ui.viewbinder.NotificationIconContainerShelfViewBinder
+import com.android.systemui.statusbar.notification.shared.NotificationsLiveDataStoreRefactor
import com.android.systemui.statusbar.notification.shelf.ui.viewbinder.NotificationShelfViewBinder
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
+import com.android.systemui.statusbar.notification.stack.ui.view.NotificationStatsLogger
import com.android.systemui.statusbar.notification.stack.ui.viewbinder.HideNotificationsBinder.bindHideList
import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationListViewModel
import com.android.systemui.statusbar.phone.NotificationIconAreaController
import com.android.systemui.util.kotlin.getOrNull
+import java.util.Optional
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.flow.combine
@@ -49,13 +52,14 @@
class NotificationListViewBinder
@Inject
constructor(
- private val viewModel: NotificationListViewModel,
@Background private val backgroundDispatcher: CoroutineDispatcher,
private val configuration: ConfigurationState,
private val falsingManager: FalsingManager,
private val iconAreaController: NotificationIconAreaController,
private val metricsLogger: MetricsLogger,
private val nicBinder: NotificationIconContainerShelfViewBinder,
+ private val loggerOptional: Optional<NotificationStatsLogger>,
+ private val viewModel: NotificationListViewModel,
) {
fun bindWhileAttached(
@@ -75,10 +79,15 @@
if (FooterViewRefactor.isEnabled) {
launch { bindFooter(view) }
launch { bindEmptyShade(view) }
- viewModel.isImportantForAccessibility.collect { isImportantForAccessibility ->
- view.setImportantForAccessibilityYesNo(isImportantForAccessibility)
+ launch {
+ viewModel.isImportantForAccessibility.collect { isImportantForAccessibility
+ ->
+ view.setImportantForAccessibilityYesNo(isImportantForAccessibility)
+ }
}
}
+
+ launch { bindLogger(view) }
}
}
}
@@ -136,4 +145,18 @@
)
}
}
+
+ private suspend fun bindLogger(view: NotificationStackScrollLayout) {
+ if (NotificationsLiveDataStoreRefactor.isEnabled) {
+ viewModel.logger.getOrNull()?.let { viewModel ->
+ loggerOptional.getOrNull()?.let { logger ->
+ NotificationStatsLoggerBinder.bindLogger(
+ view,
+ logger,
+ viewModel,
+ )
+ }
+ }
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationStatsLoggerBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationStatsLoggerBinder.kt
new file mode 100644
index 0000000..a05ad6e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationStatsLoggerBinder.kt
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.stack.ui.viewbinder
+
+import com.android.systemui.common.coroutine.ConflatedCallbackFlow
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout
+import com.android.systemui.statusbar.notification.stack.ui.view.NotificationStatsLogger
+import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationLoggerViewModel
+import com.android.systemui.util.kotlin.Utils
+import com.android.systemui.util.kotlin.sample
+import com.android.systemui.util.kotlin.throttle
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.collectLatest
+import kotlinx.coroutines.flow.combine
+
+/**
+ * Binds a [NotificationStatsLogger] to its [NotificationLoggerViewModel], and wires in
+ * [NotificationStackScrollLayout.OnNotificationLocationsChangedListener] updates to it.
+ */
+object NotificationStatsLoggerBinder {
+
+ /** minimum delay in ms between Notification location updates */
+ private const val NOTIFICATION_UPDATE_PERIOD_MS = 500L
+
+ suspend fun bindLogger(
+ view: NotificationStackScrollLayout,
+ logger: NotificationStatsLogger,
+ viewModel: NotificationLoggerViewModel,
+ ) {
+ viewModel.isLockscreenOrShadeInteractive
+ .sample(
+ combine(viewModel.isOnLockScreen, viewModel.activeNotifications, ::Pair),
+ Utils.Companion::toTriple
+ )
+ .collectLatest { (isPanelInteractive, isOnLockScreen, notifications) ->
+ if (isPanelInteractive) {
+ logger.onLockscreenOrShadeInteractive(
+ isOnLockScreen = isOnLockScreen,
+ activeNotifications = notifications,
+ )
+ view.onNotificationsUpdated
+ // Delay the updates with [NOTIFICATION_UPDATES_PERIOD_MS]. If the original
+ // flow emits more than once during this period, only the latest value is
+ // emitted, meaning that we won't log the intermediate Notification states.
+ .throttle(NOTIFICATION_UPDATE_PERIOD_MS)
+ .sample(viewModel.activeNotificationRanks, ::Pair)
+ .collect { (locationsProvider, notificationRanks) ->
+ logger.onNotificationListUpdated(locationsProvider, notificationRanks)
+ }
+ } else {
+ logger.onLockscreenOrShadeNotInteractive(
+ activeNotifications = notifications,
+ )
+ }
+ }
+ }
+}
+
+private val NotificationStackScrollLayout.onNotificationsUpdated
+ get() =
+ ConflatedCallbackFlow.conflatedCallbackFlow {
+ val callback =
+ NotificationStackScrollLayout.OnNotificationLocationsChangedListener { callable ->
+ trySend(callable)
+ }
+ setNotificationLocationsChangedListener(callback)
+ awaitClose { setNotificationLocationsChangedListener(null) }
+ }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModel.kt
index 569ae24..86c0a678 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModel.kt
@@ -40,6 +40,7 @@
val shelf: NotificationShelfViewModel,
val hideListViewModel: HideListViewModel,
val footer: Optional<FooterViewModel>,
+ val logger: Optional<NotificationLoggerViewModel>,
activeNotificationsInteractor: ActiveNotificationsInteractor,
keyguardTransitionInteractor: KeyguardTransitionInteractor,
seenNotificationsInteractor: SeenNotificationsInteractor,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationLoggerViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationLoggerViewModel.kt
new file mode 100644
index 0000000..0901a7f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationLoggerViewModel.kt
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.stack.ui.viewmodel
+
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
+import com.android.systemui.scene.domain.interactor.WindowRootViewVisibilityInteractor
+import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor
+import com.android.systemui.statusbar.notification.shared.ActiveNotificationModel
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.map
+
+class NotificationLoggerViewModel
+@Inject
+constructor(
+ activeNotificationsInteractor: ActiveNotificationsInteractor,
+ keyguardInteractor: KeyguardInteractor,
+ windowRootViewVisibilityInteractor: WindowRootViewVisibilityInteractor,
+) {
+ val activeNotifications: Flow<List<ActiveNotificationModel>> =
+ activeNotificationsInteractor.allRepresentativeNotifications.map { it.values.toList() }
+
+ val activeNotificationRanks: Flow<Map<String, Int>> =
+ activeNotificationsInteractor.activeNotificationRanks
+
+ val isLockscreenOrShadeInteractive: Flow<Boolean> =
+ windowRootViewVisibilityInteractor.isLockscreenOrShadeVisibleAndInteractive
+
+ val isOnLockScreen: Flow<Boolean> = keyguardInteractor.isKeyguardShowing
+}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java
index 3d7d701..d8ef981 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java
@@ -175,7 +175,7 @@
mPrimaryBouncerInteractor,
mContext,
() -> mDeviceEntryInteractor,
- mSceneTestUtils.getSceneContainerFlags()
+ mSceneTestUtils.getFakeSceneContainerFlags()
);
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerTest.java
index 93a5393..132bdb5 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerTest.java
@@ -373,7 +373,7 @@
@Test
public void longPress_showBouncer_sceneContainerNotEnabled() {
init(/* useMigrationFlag= */ false);
- mSceneTestUtils.getSceneContainerFlags().setEnabled(false);
+ mSceneTestUtils.getFakeSceneContainerFlags().setEnabled(false);
when(mFalsingManager.isFalseLongTap(anyInt())).thenReturn(false);
// WHEN longPress
@@ -387,7 +387,7 @@
@Test
public void longPress_showBouncer() {
init(/* useMigrationFlag= */ false);
- mSceneTestUtils.getSceneContainerFlags().setEnabled(true);
+ mSceneTestUtils.getFakeSceneContainerFlags().setEnabled(true);
when(mFalsingManager.isFalseLongTap(anyInt())).thenReturn(false);
// WHEN longPress
@@ -401,7 +401,7 @@
@Test
public void longPress_falsingTriggered_doesNotShowBouncer() {
init(/* useMigrationFlag= */ false);
- mSceneTestUtils.getSceneContainerFlags().setEnabled(true);
+ mSceneTestUtils.getFakeSceneContainerFlags().setEnabled(true);
when(mFalsingManager.isFalseLongTap(anyInt())).thenReturn(true);
// WHEN longPress
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsBpViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsBpViewControllerTest.kt
index 647dae6..13306be 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsBpViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsBpViewControllerTest.kt
@@ -20,6 +20,7 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor
import com.android.systemui.dump.DumpManager
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.shade.domain.interactor.ShadeInteractor
@@ -44,6 +45,7 @@
@Mock lateinit var shadeInteractor: ShadeInteractor
@Mock lateinit var systemUIDialogManager: SystemUIDialogManager
@Mock lateinit var dumpManager: DumpManager
+ @Mock lateinit var udfpsOverlayInteractor: UdfpsOverlayInteractor
private lateinit var udfpsBpViewController: UdfpsBpViewController
@@ -55,7 +57,8 @@
statusBarStateController,
shadeInteractor,
systemUIDialogManager,
- dumpManager
+ dumpManager,
+ udfpsOverlayInteractor,
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/LogContextInteractorImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/LogContextInteractorImplTest.kt
index 8f8004f..b1e471a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/LogContextInteractorImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/LogContextInteractorImplTest.kt
@@ -5,6 +5,7 @@
import android.hardware.biometrics.IBiometricContextListener.FoldState
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.biometrics.AuthController
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
@@ -17,6 +18,7 @@
import com.android.systemui.unfold.updates.FOLD_UPDATE_START_CLOSING
import com.android.systemui.unfold.updates.FOLD_UPDATE_START_OPENING
import com.android.systemui.unfold.updates.FoldStateProvider
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import com.android.systemui.util.mockito.withArgCaptor
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -42,7 +44,10 @@
private val testScope = TestScope()
@Mock private lateinit var foldProvider: FoldStateProvider
+ @Mock private lateinit var authController: AuthController
+ @Mock private lateinit var selectedUserInteractor: SelectedUserInteractor
+ private lateinit var udfpsOverlayInteractor: UdfpsOverlayInteractor
private lateinit var keyguardTransitionRepository: FakeKeyguardTransitionRepository
private lateinit var interactor: LogContextInteractorImpl
@@ -50,6 +55,13 @@
@Before
fun setup() {
keyguardTransitionRepository = FakeKeyguardTransitionRepository()
+ udfpsOverlayInteractor =
+ UdfpsOverlayInteractor(
+ context,
+ authController,
+ selectedUserInteractor,
+ testScope.backgroundScope,
+ )
interactor =
LogContextInteractorImpl(
testScope.backgroundScope,
@@ -59,6 +71,7 @@
scope = testScope.backgroundScope,
)
.keyguardTransitionInteractor,
+ udfpsOverlayInteractor,
)
}
@@ -162,6 +175,18 @@
}
@Test
+ fun isHardwareIgnoringTouchesChanges() =
+ testScope.runTest {
+ val isHardwareIgnoringTouches by collectLastValue(interactor.isHardwareIgnoringTouches)
+
+ udfpsOverlayInteractor.setHandleTouches(true)
+ assertThat(isHardwareIgnoringTouches).isFalse()
+
+ udfpsOverlayInteractor.setHandleTouches(false)
+ assertThat(isHardwareIgnoringTouches).isTrue()
+ }
+
+ @Test
fun foldStateChanges() =
testScope.runTest {
val foldState = collectLastValue(interactor.foldState)
@@ -195,6 +220,7 @@
var folded: Int? = null
var displayState: Int? = null
+ var ignoreTouches: Boolean? = null
val job =
interactor.addBiometricContextListener(
object : IBiometricContextListener.Stub() {
@@ -205,12 +231,17 @@
override fun onDisplayStateChanged(newDisplayState: Int) {
displayState = newDisplayState
}
+
+ override fun onHardwareIgnoreTouchesChanged(newIgnoreTouches: Boolean) {
+ ignoreTouches = newIgnoreTouches
+ }
}
)
runCurrent()
assertThat(folded).isEqualTo(FoldState.FULLY_CLOSED)
assertThat(displayState).isEqualTo(AuthenticateOptions.DISPLAY_STATE_AOD)
+ assertThat(ignoreTouches).isFalse()
foldListener.onFoldUpdate(FOLD_UPDATE_START_OPENING)
foldListener.onFoldUpdate(FOLD_UPDATE_FINISH_HALF_OPEN)
@@ -220,6 +251,11 @@
assertThat(folded).isEqualTo(FoldState.HALF_OPENED)
assertThat(displayState).isEqualTo(AuthenticateOptions.DISPLAY_STATE_LOCKSCREEN)
+ udfpsOverlayInteractor.setHandleTouches(false)
+ runCurrent()
+
+ assertThat(ignoreTouches).isTrue()
+
job.cancel()
// stale updates should be ignored
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractorTest.kt
index 6a68672..c0e108e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractorTest.kt
@@ -68,7 +68,7 @@
@Test
fun testShouldInterceptTouch() =
testScope.runTest {
- createUdpfsOverlayInteractor()
+ createUdfpsOverlayInteractor()
// When fingerprint enrolled and touch is within bounds
verify(authController).addCallback(authControllerCallback.capture())
@@ -92,7 +92,7 @@
@Test
fun testUdfpsOverlayParamsChange() =
testScope.runTest {
- createUdpfsOverlayInteractor()
+ createUdfpsOverlayInteractor()
val udfpsOverlayParams = collectLastValue(underTest.udfpsOverlayParams)
runCurrent()
@@ -105,7 +105,7 @@
assertThat(udfpsOverlayParams()).isEqualTo(firstParams)
}
- private fun createUdpfsOverlayInteractor() {
+ private fun createUdfpsOverlayInteractor() {
underTest =
UdfpsOverlayInteractor(
context,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryHapticsInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryHapticsInteractorTest.kt
index d5c3641..0dfdeca 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryHapticsInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryHapticsInteractorTest.kt
@@ -36,9 +36,9 @@
import com.android.systemui.power.shared.model.WakeSleepReason
import com.android.systemui.power.shared.model.WakefulnessState
import com.android.systemui.testKosmos
-import com.android.systemui.util.time.fakeSystemClock
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.advanceTimeBy
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Test
@@ -72,8 +72,8 @@
// It's been 10 seconds since the last power button wakeup
setAwakeFromPowerButton()
+ advanceTimeBy(10000)
runCurrent()
- kosmos.fakeSystemClock.setUptimeMillis(kosmos.fakeSystemClock.uptimeMillis() + 10000)
enterDeviceFromBiometricUnlock()
assertThat(playSuccessHaptic).isNotNull()
@@ -89,8 +89,8 @@
// It's been 10 seconds since the last power button wakeup
setAwakeFromPowerButton()
+ advanceTimeBy(10000)
runCurrent()
- kosmos.fakeSystemClock.setUptimeMillis(kosmos.fakeSystemClock.uptimeMillis() + 10000)
enterDeviceFromBiometricUnlock()
assertThat(playSuccessHaptic).isNull()
@@ -106,8 +106,8 @@
// It's only been 50ms since the last power button wakeup
setAwakeFromPowerButton()
+ advanceTimeBy(50)
runCurrent()
- kosmos.fakeSystemClock.setUptimeMillis(kosmos.fakeSystemClock.uptimeMillis() + 50)
enterDeviceFromBiometricUnlock()
assertThat(playSuccessHaptic).isNull()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/shared/flag/SceneContainerFlagsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/scene/shared/flag/SceneContainerFlagsTest.kt
index 9941661..543f6c9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/scene/shared/flag/SceneContainerFlagsTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/scene/shared/flag/SceneContainerFlagsTest.kt
@@ -16,17 +16,13 @@
package com.android.systemui.scene.shared.flag
+import android.platform.test.annotations.DisableFlags
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.systemui.Flags.FLAG_SCENE_CONTAINER
import com.android.systemui.SysuiTestCase
-import com.android.systemui.compose.ComposeFacade
-import com.android.systemui.flags.Flags
-import com.android.systemui.flags.setFlagValue
-import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl
-import com.android.systemui.media.controls.util.MediaInSceneContainerFlag
+import com.android.systemui.flags.EnableSceneContainer
import com.google.common.truth.Truth
-import org.junit.Assume
-import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -34,45 +30,17 @@
@RunWith(AndroidJUnit4::class)
internal class SceneContainerFlagsTest : SysuiTestCase() {
- @Before
- fun setUp() {
- // TODO(b/283300105): remove this reflection setting once the hard-coded
- // Flags.SCENE_CONTAINER_ENABLED is no longer needed.
- val field = Flags::class.java.getField("SCENE_CONTAINER_ENABLED")
- field.isAccessible = true
- field.set(null, true) // note: this does not work with multivalent tests
- }
-
- private fun setAconfigFlagsEnabled(enabled: Boolean) {
- listOf(
- com.android.systemui.Flags.FLAG_SCENE_CONTAINER,
- com.android.systemui.Flags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR,
- KeyguardShadeMigrationNssl.FLAG_NAME,
- MediaInSceneContainerFlag.FLAG_NAME,
- )
- .forEach { flagName -> mSetFlagsRule.setFlagValue(flagName, enabled) }
- }
-
@Test
+ @DisableFlags(FLAG_SCENE_CONTAINER)
fun isNotEnabled_withoutAconfigFlags() {
- setAconfigFlagsEnabled(false)
Truth.assertThat(SceneContainerFlag.isEnabled).isEqualTo(false)
Truth.assertThat(SceneContainerFlagsImpl().isEnabled()).isEqualTo(false)
}
@Test
- fun isEnabled_withAconfigFlags_withCompose() {
- Assume.assumeTrue(ComposeFacade.isComposeAvailable())
- setAconfigFlagsEnabled(true)
+ @EnableSceneContainer
+ fun isEnabled_withAconfigFlags() {
Truth.assertThat(SceneContainerFlag.isEnabled).isEqualTo(true)
Truth.assertThat(SceneContainerFlagsImpl().isEnabled()).isEqualTo(true)
}
-
- @Test
- fun isNotEnabled_withAconfigFlags_withoutCompose() {
- Assume.assumeFalse(ComposeFacade.isComposeAvailable())
- setAconfigFlagsEnabled(true)
- Truth.assertThat(SceneContainerFlag.isEnabled).isEqualTo(false)
- Truth.assertThat(SceneContainerFlagsImpl().isEnabled()).isEqualTo(false)
- }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java
index 791c080..971c8a3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java
@@ -131,7 +131,6 @@
@Spy private final NotificationShadeWindowView mNotificationShadeWindowView = spy(
new NotificationShadeWindowView(mContext, null));
@Mock private IActivityManager mActivityManager;
- @Mock private SysuiStatusBarStateController mStatusBarStateController;
@Mock private ConfigurationController mConfigurationController;
@Mock private KeyguardViewMediator mKeyguardViewMediator;
@Mock private KeyguardBypassController mKeyguardBypassController;
@@ -140,7 +139,6 @@
@Mock private DumpManager mDumpManager;
@Mock private KeyguardSecurityModel mKeyguardSecurityModel;
@Mock private KeyguardStateController mKeyguardStateController;
- @Mock private ScreenOffAnimationController mScreenOffAnimationController;
@Mock private AuthController mAuthController;
@Mock private ShadeWindowLogger mShadeWindowLogger;
@Mock private SelectedUserInteractor mSelectedUserInteractor;
@@ -160,6 +158,8 @@
private float mPreferredRefreshRate = -1;
private FromLockscreenTransitionInteractor mFromLockscreenTransitionInteractor;
private FromPrimaryBouncerTransitionInteractor mFromPrimaryBouncerTransitionInteractor;
+ private ScreenOffAnimationController mScreenOffAnimationController;
+ private SysuiStatusBarStateController mStatusBarStateController;
@Before
public void setUp() {
@@ -178,11 +178,9 @@
FakeFeatureFlagsClassic featureFlags = new FakeFeatureFlagsClassic();
FakeShadeRepository shadeRepository = new FakeShadeRepository();
- PowerInteractor powerInteractor = mUtils.powerInteractor(
- mUtils.getPowerRepository(),
- mUtils.falsingCollector(),
- mScreenOffAnimationController,
- mStatusBarStateController);
+ mScreenOffAnimationController = mUtils.getScreenOffAnimationController();
+ mStatusBarStateController = spy(mUtils.getStatusBarStateController());
+ PowerInteractor powerInteractor = mUtils.powerInteractor();
SceneInteractor sceneInteractor = new SceneInteractor(
mTestScope.getBackgroundScope(),
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 ea3caa3..697b05a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerLegacyTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerLegacyTest.kt
@@ -26,6 +26,7 @@
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.constraintlayout.widget.ConstraintSet
import androidx.test.filters.SmallTest
+import com.android.systemui.Flags.FLAG_CENTRALIZED_STATUS_BAR_DIMENS_REFACTOR
import com.android.systemui.SysuiTestCase
import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.Flags
@@ -167,10 +168,15 @@
}
@Test
- fun testLargeScreen_updateResources_splitShadeHeightIsSet() {
+ fun testLargeScreen_updateResources_refactorFlagOff_splitShadeHeightIsSetBasedOnResource() {
+ val headerResourceHeight = 20
+ val headerHelperHeight = 30
+ mSetFlagsRule.disableFlags(FLAG_CENTRALIZED_STATUS_BAR_DIMENS_REFACTOR)
+ 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, 20)
+ 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)
@@ -180,7 +186,31 @@
val captor = ArgumentCaptor.forClass(ConstraintSet::class.java)
verify(view).applyConstraints(capture(captor))
- assertThat(captor.value.getHeight(R.id.split_shade_status_bar)).isEqualTo(20)
+ assertThat(captor.value.getHeight(R.id.split_shade_status_bar))
+ .isEqualTo(headerResourceHeight)
+ }
+
+ @Test
+ fun testLargeScreen_updateResources_refactorFlagOn_splitShadeHeightIsSetBasedOnHelper() {
+ val headerResourceHeight = 20
+ val headerHelperHeight = 30
+ mSetFlagsRule.enableFlags(FLAG_CENTRALIZED_STATUS_BAR_DIMENS_REFACTOR)
+ 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(headerHelperHeight)
}
@Test
@@ -416,10 +446,14 @@
}
@Test
- fun testLargeScreenLayout_qsAndNotifsTopMarginIsOfHeaderHeight() {
+ fun testLargeScreenLayout_refactorFlagOff_qsAndNotifsTopMarginIsOfHeaderHeightResource() {
+ mSetFlagsRule.disableFlags(FLAG_CENTRALIZED_STATUS_BAR_DIMENS_REFACTOR)
setLargeScreen()
- val largeScreenHeaderHeight = 100
- overrideResource(R.dimen.large_screen_shade_header_height, largeScreenHeaderHeight)
+ 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)
@@ -428,9 +462,31 @@
underTest.updateResources()
assertThat(getConstraintSetLayout(R.id.qs_frame).topMargin)
- .isEqualTo(largeScreenHeaderHeight)
+ .isEqualTo(largeScreenHeaderResourceHeight)
assertThat(getConstraintSetLayout(R.id.notification_stack_scroller).topMargin)
- .isEqualTo(largeScreenHeaderHeight)
+ .isEqualTo(largeScreenHeaderResourceHeight)
+ }
+
+ @Test
+ fun testLargeScreenLayout_refactorFlagOn_qsAndNotifsTopMarginIsOfHeaderHeightHelper() {
+ mSetFlagsRule.enableFlags(FLAG_CENTRALIZED_STATUS_BAR_DIMENS_REFACTOR)
+ 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(largeScreenHeaderHelperHeight)
+ assertThat(getConstraintSetLayout(R.id.notification_stack_scroller).topMargin)
+ .isEqualTo(largeScreenHeaderHelperHeight)
}
@Test
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 c1bc303..e66251a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerTest.kt
@@ -26,6 +26,7 @@
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.constraintlayout.widget.ConstraintSet
import androidx.test.filters.SmallTest
+import com.android.systemui.Flags.FLAG_CENTRALIZED_STATUS_BAR_DIMENS_REFACTOR
import com.android.systemui.SysuiTestCase
import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.Flags
@@ -166,10 +167,14 @@
}
@Test
- fun testLargeScreen_updateResources_splitShadeHeightIsSet() {
+ fun testLargeScreen_updateResources_refactorFlagOff_splitShadeHeightIsSet_basedOnResource() {
+ mSetFlagsRule.disableFlags(FLAG_CENTRALIZED_STATUS_BAR_DIMENS_REFACTOR)
+ 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, 20)
+ 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)
@@ -179,7 +184,28 @@
val captor = ArgumentCaptor.forClass(ConstraintSet::class.java)
verify(view).applyConstraints(capture(captor))
- assertThat(captor.value.getHeight(R.id.split_shade_status_bar)).isEqualTo(20)
+ assertThat(captor.value.getHeight(R.id.split_shade_status_bar)).isEqualTo(resourceHeight)
+ }
+
+ @Test
+ fun testLargeScreen_updateResources_refactorFlagOn_splitShadeHeightIsSet_basedOnHelper() {
+ mSetFlagsRule.enableFlags(FLAG_CENTRALIZED_STATUS_BAR_DIMENS_REFACTOR)
+ 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(helperHeight)
}
@Test
@@ -404,10 +430,14 @@
}
@Test
- fun testLargeScreenLayout_qsAndNotifsTopMarginIsOfHeaderHeight() {
+ fun testLargeScreenLayout_refactorFlagOff_qsAndNotifsTopMarginIsOfHeaderResourceHeight() {
+ mSetFlagsRule.disableFlags(FLAG_CENTRALIZED_STATUS_BAR_DIMENS_REFACTOR)
setLargeScreen()
- val largeScreenHeaderHeight = 100
- overrideResource(R.dimen.large_screen_shade_header_height, largeScreenHeaderHeight)
+ 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)
@@ -416,7 +446,27 @@
underTest.updateResources()
assertThat(getConstraintSetLayout(R.id.qs_frame).topMargin)
- .isEqualTo(largeScreenHeaderHeight)
+ .isEqualTo(largeScreenHeaderResourceHeight)
+ }
+
+ @Test
+ fun testLargeScreenLayout_refactorFlagOn_qsAndNotifsTopMarginIsOfHeaderHelperHeight() {
+ mSetFlagsRule.enableFlags(FLAG_CENTRALIZED_STATUS_BAR_DIMENS_REFACTOR)
+ 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(largeScreenHeaderHelperHeight)
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java
index a369f82..ca68fd8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java
@@ -85,7 +85,6 @@
import com.android.systemui.statusbar.NotificationShadeDepthController;
import com.android.systemui.statusbar.PulseExpansionHandler;
import com.android.systemui.statusbar.QsFrameTranslateController;
-import com.android.systemui.statusbar.StatusBarStateControllerImpl;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.disableflags.data.repository.FakeDisableFlagsRepository;
import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationListRepository;
@@ -99,7 +98,6 @@
import com.android.systemui.statusbar.phone.KeyguardStatusBarView;
import com.android.systemui.statusbar.phone.LightBarController;
import com.android.systemui.statusbar.phone.LockscreenGestureLogger;
-import com.android.systemui.statusbar.phone.ScreenOffAnimationController;
import com.android.systemui.statusbar.phone.ScrimController;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.statusbar.phone.StatusBarTouchableRegionManager;
@@ -172,7 +170,6 @@
@Mock protected LockscreenGestureLogger mLockscreenGestureLogger;
@Mock protected MetricsLogger mMetricsLogger;
@Mock protected FeatureFlags mFeatureFlags;
- @Mock protected InteractionJankMonitor mInteractionJankMonitor;
@Mock protected ShadeLogger mShadeLogger;
@Mock protected DumpManager mDumpManager;
@Mock protected UiEventLogger mUiEventLogger;
@@ -186,6 +183,7 @@
protected FakeKeyguardRepository mKeyguardRepository = new FakeKeyguardRepository();
protected FakeShadeRepository mShadeRepository = new FakeShadeRepository();
+ protected InteractionJankMonitor mInteractionJankMonitor;
protected SysuiStatusBarStateController mStatusBarStateController;
protected ShadeInteractor mShadeInteractor;
@@ -205,8 +203,8 @@
public void setup() {
MockitoAnnotations.initMocks(this);
when(mPanelViewControllerLazy.get()).thenReturn(mNotificationPanelViewController);
- mStatusBarStateController = new StatusBarStateControllerImpl(mUiEventLogger,
- mInteractionJankMonitor, mock(JavaAdapter.class), () -> mShadeInteractor);
+ mStatusBarStateController = mUtils.getStatusBarStateController();
+ mInteractionJankMonitor = mUtils.getInteractionJankMonitor();
FakeDeviceProvisioningRepository deviceProvisioningRepository =
new FakeDeviceProvisioningRepository();
@@ -214,11 +212,7 @@
FakeFeatureFlagsClassic featureFlags = new FakeFeatureFlagsClassic();
FakeConfigurationRepository configurationRepository = new FakeConfigurationRepository();
- PowerInteractor powerInteractor = mUtils.powerInteractor(
- mUtils.getPowerRepository(),
- mUtils.falsingCollector(),
- mock(ScreenOffAnimationController.class),
- mStatusBarStateController);
+ PowerInteractor powerInteractor = mUtils.powerInteractor();
SceneInteractor sceneInteractor = new SceneInteractor(
mTestScope.getBackgroundScope(),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractorTest.kt
index 4ab3cd4..b3b10eb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractorTest.kt
@@ -74,6 +74,18 @@
}
@Test
+ fun testActiveNotificationRanks_sizeMatches() {
+ testComponent.runTest {
+ val activeNotificationRanks by collectLastValue(underTest.activeNotificationRanks)
+
+ activeNotificationListRepository.setActiveNotifs(5)
+ runCurrent()
+
+ assertThat(activeNotificationRanks!!.size).isEqualTo(5)
+ }
+ }
+
+ @Test
fun testHasClearableNotifications_whenHasClearableAlertingNotifs() =
testComponent.runTest {
val hasClearable by collectLastValue(underTest.hasClearableNotifications)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationsListInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationsListInteractorTest.kt
index 6374d5e..334776c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationsListInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationsListInteractorTest.kt
@@ -15,10 +15,12 @@
package com.android.systemui.statusbar.notification.domain.interactor
+import android.service.notification.StatusBarNotification
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.statusbar.notification.collection.ListEntry
+import com.android.systemui.statusbar.RankingBuilder
+import com.android.systemui.statusbar.notification.collection.GroupEntry
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationListRepository
import com.android.systemui.statusbar.notification.shared.byKey
@@ -49,22 +51,101 @@
testScope.runTest {
val notifs by collectLastValue(notifsInteractor.topLevelRepresentativeNotifications)
val keys = (1..50).shuffled().map { "$it" }
- val entries =
- keys.map {
- mock<ListEntry> {
- val mockRep =
- mock<NotificationEntry> {
- whenever(key).thenReturn(it)
- whenever(sbn).thenReturn(mock())
- whenever(icons).thenReturn(mock())
- }
- whenever(representativeEntry).thenReturn(mockRep)
- }
- }
+ val entries = keys.map { mockNotificationEntry(key = it) }
underTest.setRenderedList(entries)
assertThat(notifs)
.comparingElementsUsing(byKey)
.containsExactlyElementsIn(keys)
.inOrder()
}
+
+ @Test
+ fun setRenderList_flatMapsRankings() =
+ testScope.runTest {
+ val ranks by collectLastValue(notifsInteractor.activeNotificationRanks)
+
+ val single = mockNotificationEntry("single", 0)
+ val group =
+ mockGroupEntry(
+ key = "group",
+ summary = mockNotificationEntry("summary", 1),
+ children =
+ listOf(
+ mockNotificationEntry("child0", 2),
+ mockNotificationEntry("child1", 3),
+ ),
+ )
+
+ underTest.setRenderedList(listOf(single, group))
+
+ assertThat(ranks)
+ .containsExactlyEntriesIn(
+ mapOf(
+ "single" to 0,
+ "summary" to 1,
+ "child0" to 2,
+ "child1" to 3,
+ )
+ )
+ }
+
+ @Test
+ fun setRenderList_singleItems_mapsRankings() =
+ testScope.runTest {
+ val actual by collectLastValue(notifsInteractor.activeNotificationRanks)
+ val expected =
+ (0..10).shuffled().mapIndexed { index, value -> "$value" to index }.toMap()
+
+ val entries = expected.map { (key, rank) -> mockNotificationEntry(key, rank) }
+
+ underTest.setRenderedList(entries)
+
+ assertThat(actual).containsAtLeastEntriesIn(expected)
+ }
+
+ @Test
+ fun setRenderList_groupWithNoSummary_flatMapsRankings() =
+ testScope.runTest {
+ val actual by collectLastValue(notifsInteractor.activeNotificationRanks)
+ val expected =
+ (0..10).shuffled().mapIndexed { index, value -> "$value" to index }.toMap()
+
+ val group =
+ mockGroupEntry(
+ key = "group",
+ summary = null,
+ children = expected.map { (key, rank) -> mockNotificationEntry(key, rank) },
+ )
+
+ underTest.setRenderedList(listOf(group))
+
+ assertThat(actual).containsAtLeastEntriesIn(expected)
+ }
+}
+
+private fun mockGroupEntry(
+ key: String,
+ summary: NotificationEntry?,
+ children: List<NotificationEntry>,
+): GroupEntry {
+ return mock<GroupEntry> {
+ whenever(this.key).thenReturn(key)
+ whenever(this.summary).thenReturn(summary)
+ whenever(this.children).thenReturn(children)
+ }
+}
+
+private fun mockNotificationEntry(key: String, rank: Int = 0): NotificationEntry {
+ val mockSbn =
+ mock<StatusBarNotification>() {
+ whenever(notification).thenReturn(mock())
+ whenever(packageName).thenReturn("com.android")
+ }
+ return mock<NotificationEntry> {
+ whenever(this.key).thenReturn(key)
+ whenever(this.icons).thenReturn(mock())
+ whenever(this.representativeEntry).thenReturn(this)
+ whenever(this.ranking).thenReturn(RankingBuilder().setRank(rank).build())
+ whenever(this.sbn).thenReturn(mockSbn)
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationPanelLoggerFake.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationPanelLoggerFake.java
index dae0aa2..d61fc05 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationPanelLoggerFake.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationPanelLoggerFake.java
@@ -34,6 +34,11 @@
}
@Override
+ public void logPanelShown(boolean isLockscreen, Notifications.NotificationList proto) {
+ mCalls.add(new CallRecord(isLockscreen, proto));
+ }
+
+ @Override
public void logPanelShown(boolean isLockscreen,
List<NotificationEntry> visibleNotifications) {
mCalls.add(new CallRecord(isLockscreen,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt
index 9547af1..8ac2a33 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt
@@ -40,12 +40,12 @@
import com.android.systemui.statusbar.notification.collection.render.FakeNodeController
import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager
import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager
-import com.android.systemui.statusbar.notification.logging.NotificationLogger
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRowController.BUBBLES_SETTING_URI
import com.android.systemui.statusbar.notification.stack.NotificationChildrenContainer
import com.android.systemui.statusbar.notification.stack.NotificationChildrenContainerLogger
import com.android.systemui.statusbar.notification.stack.NotificationListContainer
+import com.android.systemui.statusbar.notification.stack.ui.view.NotificationRowStatsLogger
import com.android.systemui.statusbar.phone.KeyguardBypassController
import com.android.systemui.statusbar.policy.HeadsUpManager
import com.android.systemui.statusbar.policy.SmartReplyConstants
@@ -92,7 +92,7 @@
private val groupMembershipManager: GroupMembershipManager = mock()
private val groupExpansionManager: GroupExpansionManager = mock()
private val rowContentBindStage: RowContentBindStage = mock()
- private val notifLogger: NotificationLogger = mock()
+ private val notifLogger: NotificationRowStatsLogger = mock()
private val headsUpManager: HeadsUpManager = mock()
private val onExpandClickListener: ExpandableNotificationRow.OnExpandClickListener = mock()
private val statusBarStateController: StatusBarStateController = mock()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/view/NotificationStatsLoggerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/view/NotificationStatsLoggerTest.kt
new file mode 100644
index 0000000..d7d1ffc
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/view/NotificationStatsLoggerTest.kt
@@ -0,0 +1,429 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.stack.ui.view
+
+import android.service.notification.notificationListenerService
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.internal.statusbar.NotificationVisibility
+import com.android.internal.statusbar.statusBarService
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.statusbar.notification.data.model.activeNotificationModel
+import com.android.systemui.statusbar.notification.logging.nano.Notifications
+import com.android.systemui.statusbar.notification.logging.notificationPanelLogger
+import com.android.systemui.statusbar.notification.stack.ExpandableViewState
+import com.android.systemui.testKosmos
+import com.android.systemui.util.mockito.argumentCaptor
+import com.android.systemui.util.mockito.eq
+import com.google.common.truth.Truth.assertThat
+import java.util.concurrent.Callable
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito.clearInvocations
+import org.mockito.Mockito.spy
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.verifyZeroInteractions
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class NotificationStatsLoggerTest : SysuiTestCase() {
+
+ private val kosmos = testKosmos()
+
+ private val testScope = kosmos.testScope
+ private val mockNotificationListenerService = kosmos.notificationListenerService
+ private val mockPanelLogger = kosmos.notificationPanelLogger
+ private val mockStatusBarService = kosmos.statusBarService
+
+ private val underTest = kosmos.notificationStatsLogger
+
+ private val visibilityArrayCaptor = argumentCaptor<Array<NotificationVisibility>>()
+ private val stringArrayCaptor = argumentCaptor<Array<String>>()
+ private val notificationListProtoCaptor = argumentCaptor<Notifications.NotificationList>()
+
+ @Test
+ fun onNotificationListUpdated_itemsAdded_logsNewlyVisibleItems() =
+ testScope.runTest {
+ // WHEN new Notifications are added
+ // AND they're visible
+ val (ranks, locations) = fakeNotificationMaps("key0", "key1")
+ val callable = Callable { locations }
+ underTest.onNotificationListUpdated(callable, ranks)
+ runCurrent()
+
+ // THEN visibility changes are reported
+ verify(mockStatusBarService)
+ .onNotificationVisibilityChanged(visibilityArrayCaptor.capture(), eq(emptyArray()))
+ verify(mockNotificationListenerService)
+ .setNotificationsShown(stringArrayCaptor.capture())
+ val loggedVisibilities = visibilityArrayCaptor.value
+ val loggedKeys = stringArrayCaptor.value
+ assertThat(loggedVisibilities).hasLength(2)
+ assertThat(loggedKeys).hasLength(2)
+ assertThat(loggedVisibilities[0]).apply {
+ isKeyEqualTo("key0")
+ isRankEqualTo(0)
+ isVisible()
+ isInMainArea()
+ isCountEqualTo(2)
+ }
+ assertThat(loggedVisibilities[1]).apply {
+ isKeyEqualTo("key1")
+ isRankEqualTo(1)
+ isVisible()
+ isInMainArea()
+ isCountEqualTo(2)
+ }
+ assertThat(loggedKeys[0]).isEqualTo("key0")
+ assertThat(loggedKeys[1]).isEqualTo("key1")
+ }
+
+ @Test
+ fun onNotificationListUpdated_itemsRemoved_logsNoLongerVisibleItems() =
+ testScope.runTest {
+ // GIVEN some visible Notifications are reported
+ val (ranks, locations) = fakeNotificationMaps("key0", "key1")
+ val callable = Callable { locations }
+ underTest.onNotificationListUpdated(callable, ranks)
+ runCurrent()
+ clearInvocations(mockStatusBarService, mockNotificationListenerService)
+
+ // WHEN the same Notifications are removed
+ val emptyCallable = Callable { emptyMap<String, Int>() }
+ underTest.onNotificationListUpdated(emptyCallable, emptyMap())
+ runCurrent()
+
+ // THEN visibility changes are reported
+ verify(mockStatusBarService)
+ .onNotificationVisibilityChanged(eq(emptyArray()), visibilityArrayCaptor.capture())
+ verifyZeroInteractions(mockNotificationListenerService)
+ val noLongerVisible = visibilityArrayCaptor.value
+ assertThat(noLongerVisible).hasLength(2)
+ assertThat(noLongerVisible[0]).apply {
+ isKeyEqualTo("key0")
+ isRankEqualTo(0)
+ notVisible()
+ isInMainArea()
+ isCountEqualTo(0)
+ }
+ assertThat(noLongerVisible[1]).apply {
+ isKeyEqualTo("key1")
+ isRankEqualTo(1)
+ notVisible()
+ isInMainArea()
+ isCountEqualTo(0)
+ }
+ }
+
+ @Test
+ fun onNotificationListUpdated_itemsBecomeInvisible_logsNoLongerVisibleItems() =
+ testScope.runTest {
+ // GIVEN some visible Notifications are reported
+ val (ranks, locations) = fakeNotificationMaps("key0", "key1")
+ val callable = Callable { locations }
+ underTest.onNotificationListUpdated(callable, ranks)
+ runCurrent()
+ clearInvocations(mockStatusBarService, mockNotificationListenerService)
+
+ // WHEN the same Notifications are becoming invisible
+ val emptyCallable = Callable { emptyMap<String, Int>() }
+ underTest.onNotificationListUpdated(emptyCallable, ranks)
+ runCurrent()
+
+ // THEN visibility changes are reported
+ verify(mockStatusBarService)
+ .onNotificationVisibilityChanged(eq(emptyArray()), visibilityArrayCaptor.capture())
+ verifyZeroInteractions(mockNotificationListenerService)
+ val noLongerVisible = visibilityArrayCaptor.value
+ assertThat(noLongerVisible).hasLength(2)
+ assertThat(noLongerVisible[0]).apply {
+ isKeyEqualTo("key0")
+ isRankEqualTo(0)
+ notVisible()
+ isInMainArea()
+ isCountEqualTo(2)
+ }
+ assertThat(noLongerVisible[1]).apply {
+ isKeyEqualTo("key1")
+ isRankEqualTo(1)
+ notVisible()
+ isInMainArea()
+ isCountEqualTo(2)
+ }
+ }
+
+ @Test
+ fun onNotificationListUpdated_itemsChangedPositions_nothingLogged() =
+ testScope.runTest {
+ // GIVEN some visible Notifications are reported
+ val (ranks, locations) = fakeNotificationMaps("key0", "key1")
+ underTest.onNotificationListUpdated({ locations }, ranks)
+ runCurrent()
+ clearInvocations(mockStatusBarService, mockNotificationListenerService)
+
+ // WHEN the reported Notifications are changing positions
+ val (newRanks, newLocations) = fakeNotificationMaps("key1", "key0")
+ underTest.onNotificationListUpdated({ newLocations }, newRanks)
+ runCurrent()
+
+ // THEN no visibility changes are reported
+ verifyZeroInteractions(mockStatusBarService, mockNotificationListenerService)
+ }
+
+ @Test
+ fun onNotificationListUpdated_calledTwice_usesTheNewCallable() =
+ testScope.runTest {
+ // GIVEN some visible Notifications are reported
+ val (ranks, locations) = fakeNotificationMaps("key0", "key1", "key2")
+ val callable = spy(Callable { locations })
+ underTest.onNotificationListUpdated(callable, ranks)
+ runCurrent()
+ clearInvocations(callable)
+
+ // WHEN a new update comes
+ val otherCallable = spy(Callable { locations })
+ underTest.onNotificationListUpdated(otherCallable, ranks)
+ runCurrent()
+
+ // THEN we call the new Callable
+ verifyZeroInteractions(callable)
+ verify(otherCallable).call()
+ }
+
+ @Test
+ fun onLockscreenOrShadeNotInteractive_logsNoLongerVisibleItems() =
+ testScope.runTest {
+ // GIVEN some visible Notifications are reported
+ val (ranks, locations) = fakeNotificationMaps("key0", "key1")
+ val callable = Callable { locations }
+ underTest.onNotificationListUpdated(callable, ranks)
+ runCurrent()
+ clearInvocations(mockStatusBarService, mockNotificationListenerService)
+
+ // WHEN the Shade becomes non interactive
+ underTest.onLockscreenOrShadeNotInteractive(emptyList())
+ runCurrent()
+
+ // THEN visibility changes are reported
+ verify(mockStatusBarService)
+ .onNotificationVisibilityChanged(eq(emptyArray()), visibilityArrayCaptor.capture())
+ verifyZeroInteractions(mockNotificationListenerService)
+ val noLongerVisible = visibilityArrayCaptor.value
+ assertThat(noLongerVisible).hasLength(2)
+ assertThat(noLongerVisible[0]).apply {
+ isKeyEqualTo("key0")
+ isRankEqualTo(0)
+ notVisible()
+ isInMainArea()
+ isCountEqualTo(0)
+ }
+ assertThat(noLongerVisible[1]).apply {
+ isKeyEqualTo("key1")
+ isRankEqualTo(1)
+ notVisible()
+ isInMainArea()
+ isCountEqualTo(0)
+ }
+ }
+
+ @Test
+ fun onLockscreenOrShadeInteractive_logsPanelShown() =
+ testScope.runTest {
+ // WHEN the Shade becomes interactive
+ underTest.onLockscreenOrShadeInteractive(
+ isOnLockScreen = true,
+ listOf(
+ activeNotificationModel(
+ key = "key0",
+ uid = 0,
+ packageName = "com.android.first"
+ ),
+ activeNotificationModel(
+ key = "key1",
+ uid = 1,
+ packageName = "com.android.second"
+ ),
+ )
+ )
+ runCurrent()
+
+ // THEN the Panel shown event is reported
+ verify(mockPanelLogger).logPanelShown(eq(true), notificationListProtoCaptor.capture())
+ val loggedNotifications = notificationListProtoCaptor.value.notifications
+ assertThat(loggedNotifications.size).isEqualTo(2)
+ with(loggedNotifications[0]) {
+ assertThat(uid).isEqualTo(0)
+ assertThat(packageName).isEqualTo("com.android.first")
+ }
+ with(loggedNotifications[1]) {
+ assertThat(uid).isEqualTo(1)
+ assertThat(packageName).isEqualTo("com.android.second")
+ }
+ }
+
+ @Test
+ fun onNotificationExpanded_visibleLocation_expansionLogged() =
+ testScope.runTest {
+ // WHEN a Notification is expanded
+ underTest.onNotificationExpansionChanged(
+ key = "key",
+ isExpanded = true,
+ location = ExpandableViewState.LOCATION_MAIN_AREA,
+ isUserAction = true
+ )
+ runCurrent()
+
+ // THEN the Expand event is reported
+ verify(mockStatusBarService)
+ .onNotificationExpansionChanged(
+ /* key = */ "key",
+ /* userAction = */ true,
+ /* expanded = */ true,
+ NotificationVisibility.NotificationLocation.LOCATION_MAIN_AREA.ordinal
+ )
+ }
+
+ @Test
+ fun onNotificationExpanded_notVisibleLocation_nothingLogged() =
+ testScope.runTest {
+ // WHEN a NOT visible Notification is expanded
+ underTest.onNotificationExpansionChanged(
+ key = "key",
+ isExpanded = true,
+ location = ExpandableViewState.LOCATION_BOTTOM_STACK_HIDDEN,
+ isUserAction = true
+ )
+ runCurrent()
+
+ // No events are reported
+ verifyZeroInteractions(mockStatusBarService)
+ }
+
+ @Test
+ fun onNotificationExpanded_notVisibleLocation_becomesVisible_expansionLogged() =
+ testScope.runTest {
+ // WHEN a NOT visible Notification is expanded
+ underTest.onNotificationExpansionChanged(
+ key = "key",
+ isExpanded = true,
+ location = ExpandableViewState.LOCATION_GONE,
+ isUserAction = true
+ )
+ runCurrent()
+
+ // AND it becomes visible
+ val (ranks, locations) = fakeNotificationMaps("key")
+ val callable = Callable { locations }
+ underTest.onNotificationListUpdated(callable, ranks)
+ runCurrent()
+
+ // THEN the Expand event is reported
+ verify(mockStatusBarService)
+ .onNotificationExpansionChanged(
+ /* key = */ "key",
+ /* userAction = */ true,
+ /* expanded = */ true,
+ NotificationVisibility.NotificationLocation.LOCATION_MAIN_AREA.ordinal
+ )
+ }
+
+ @Test
+ fun onNotificationCollapsed_isFirstInteraction_nothingLogged() =
+ testScope.runTest {
+ // WHEN a Notification is collapsed, and it is the first interaction
+ underTest.onNotificationExpansionChanged(
+ key = "key",
+ isExpanded = false,
+ location = ExpandableViewState.LOCATION_MAIN_AREA,
+ isUserAction = false
+ )
+ runCurrent()
+
+ // THEN no events are reported, because we consider the Notification initially
+ // collapsed, so only expanded is logged in the first time.
+ verifyZeroInteractions(mockStatusBarService)
+ }
+
+ @Test
+ fun onNotificationExpandedAndCollapsed_expansionChangesLogged() =
+ testScope.runTest {
+ // GIVEN a Notification is expanded
+ underTest.onNotificationExpansionChanged(
+ key = "key",
+ isExpanded = true,
+ location = ExpandableViewState.LOCATION_MAIN_AREA,
+ isUserAction = true
+ )
+ runCurrent()
+
+ // WHEN the Notification is collapsed
+ verify(mockStatusBarService)
+ .onNotificationExpansionChanged(
+ /* key = */ "key",
+ /* userAction = */ true,
+ /* expanded = */ true,
+ NotificationVisibility.NotificationLocation.LOCATION_MAIN_AREA.ordinal
+ )
+
+ // AND the Notification is expanded again
+ underTest.onNotificationExpansionChanged(
+ key = "key",
+ isExpanded = false,
+ location = ExpandableViewState.LOCATION_MAIN_AREA,
+ isUserAction = true
+ )
+ runCurrent()
+
+ // THEN the expansion changes are logged
+ verify(mockStatusBarService)
+ .onNotificationExpansionChanged(
+ /* key = */ "key",
+ /* userAction = */ true,
+ /* expanded = */ false,
+ NotificationVisibility.NotificationLocation.LOCATION_MAIN_AREA.ordinal
+ )
+ }
+
+ private fun fakeNotificationMaps(
+ vararg keys: String
+ ): Pair<Map<String, Int>, Map<String, Int>> {
+ val ranks: Map<String, Int> = keys.mapIndexed { index, key -> key to index }.toMap()
+ val locations: Map<String, Int> =
+ keys.associateWith { ExpandableViewState.LOCATION_MAIN_AREA }
+
+ return Pair(ranks, locations)
+ }
+
+ private fun assertThat(visibility: NotificationVisibility) =
+ NotificationVisibilitySubject(visibility)
+}
+
+private class NotificationVisibilitySubject(private val visibility: NotificationVisibility) {
+ fun isKeyEqualTo(key: String) = assertThat(visibility.key).isEqualTo(key)
+ fun isRankEqualTo(rank: Int) = assertThat(visibility.rank).isEqualTo(rank)
+ fun isCountEqualTo(count: Int) = assertThat(visibility.count).isEqualTo(count)
+ fun isVisible() = assertThat(this.visibility.visible).isTrue()
+ fun notVisible() = assertThat(this.visibility.visible).isFalse()
+ fun isInMainArea() =
+ assertThat(this.visibility.location)
+ .isEqualTo(NotificationVisibility.NotificationLocation.LOCATION_MAIN_AREA)
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelTest.kt
index c17a8ef..4188c5d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelTest.kt
@@ -38,6 +38,7 @@
import com.android.systemui.runCurrent
import com.android.systemui.runTest
import com.android.systemui.shade.data.repository.FakeShadeRepository
+import com.android.systemui.statusbar.notification.dagger.NotificationStatsLoggerModule
import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationListRepository
import com.android.systemui.statusbar.notification.data.repository.setActiveNotifs
import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor
@@ -71,6 +72,7 @@
FooterViewModelModule::class,
HeadlessSystemUserModeModule::class,
UnfoldTransitionModule.Bindings::class,
+ NotificationStatsLoggerModule::class,
]
)
interface TestComponent : SysUITestComponent<NotificationListViewModel> {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationLoggerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationLoggerViewModelTest.kt
new file mode 100644
index 0000000..e9d88cc
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationLoggerViewModelTest.kt
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.stack.ui.viewmodel
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAsleepForTest
+import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAwakeForTest
+import com.android.systemui.power.domain.interactor.powerInteractor
+import com.android.systemui.scene.data.repository.windowRootViewVisibilityRepository
+import com.android.systemui.statusbar.notification.data.repository.activeNotificationListRepository
+import com.android.systemui.statusbar.notification.data.repository.setActiveNotifs
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class NotificationLoggerViewModelTest : SysuiTestCase() {
+
+ private val kosmos = testKosmos()
+
+ private val testScope = kosmos.testScope
+ private val activeNotificationListRepository = kosmos.activeNotificationListRepository
+ private val keyguardRepository = kosmos.fakeKeyguardRepository
+ private val powerInteractor = kosmos.powerInteractor
+ private val windowRootViewVisibilityRepository = kosmos.windowRootViewVisibilityRepository
+
+ private val underTest = kosmos.notificationListLoggerViewModel
+
+ @Test
+ fun isLockscreenOrShadeInteractive_deviceActiveAndShadeIsInteractive_true() =
+ testScope.runTest {
+ powerInteractor.setAwakeForTest()
+ windowRootViewVisibilityRepository.setIsLockscreenOrShadeVisible(true)
+
+ val actual by collectLastValue(underTest.isLockscreenOrShadeInteractive)
+
+ assertThat(actual).isTrue()
+ }
+
+ @Test
+ fun isLockscreenOrShadeInteractive_deviceIsAsleepAndShadeIsInteractive_false() =
+ testScope.runTest {
+ powerInteractor.setAsleepForTest()
+ windowRootViewVisibilityRepository.setIsLockscreenOrShadeVisible(true)
+
+ val actual by collectLastValue(underTest.isLockscreenOrShadeInteractive)
+
+ assertThat(actual).isFalse()
+ }
+
+ @Test
+ fun isLockscreenOrShadeInteractive_deviceActiveAndShadeIsNotInteractive_false() =
+ testScope.runTest {
+ powerInteractor.setAwakeForTest()
+ windowRootViewVisibilityRepository.setIsLockscreenOrShadeVisible(false)
+
+ val actual by collectLastValue(underTest.isLockscreenOrShadeInteractive)
+
+ assertThat(actual).isFalse()
+ }
+
+ @Test
+ fun activeNotifications_hasNotifications() =
+ testScope.runTest {
+ activeNotificationListRepository.setActiveNotifs(5)
+
+ val notifs by collectLastValue(underTest.activeNotifications)
+
+ assertThat(notifs).hasSize(5)
+ requireNotNull(notifs).forEachIndexed { i, notif ->
+ assertThat(notif.key).isEqualTo("$i")
+ }
+ }
+
+ @Test
+ fun activeNotifications_isEmpty() =
+ testScope.runTest {
+ activeNotificationListRepository.setActiveNotifs(0)
+
+ val notifications by collectLastValue(underTest.activeNotifications)
+
+ assertThat(notifications).isEmpty()
+ }
+
+ @Test
+ fun activeNotificationRanks_hasNotifications() =
+ testScope.runTest {
+ val keys = (0..4).map { "$it" }
+ activeNotificationListRepository.setActiveNotifs(5)
+
+ val rankingsMap by collectLastValue(underTest.activeNotificationRanks)
+
+ assertThat(rankingsMap).hasSize(5)
+ keys.forEachIndexed { rank, key -> assertThat(rankingsMap).containsEntry(key, rank) }
+ }
+
+ @Test
+ fun activeNotificationRanks_isEmpty() =
+ testScope.runTest {
+ activeNotificationListRepository.setActiveNotifs(0)
+
+ val rankingsMap by collectLastValue(underTest.activeNotificationRanks)
+
+ assertThat(rankingsMap).isEmpty()
+ }
+
+ @Test
+ fun isOnLockScreen_true() =
+ testScope.runTest {
+ keyguardRepository.setKeyguardShowing(true)
+
+ val isOnLockScreen by collectLastValue(underTest.isOnLockScreen)
+
+ assertThat(isOnLockScreen).isTrue()
+ }
+ @Test
+ fun isOnLockScreen_false() =
+ testScope.runTest {
+ keyguardRepository.setKeyguardShowing(false)
+
+ val isOnLockScreen by collectLastValue(underTest.isOnLockScreen)
+
+ assertThat(isOnLockScreen).isFalse()
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
index 20020f2..9f15b05 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
@@ -21,6 +21,7 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.systemui.Flags.FLAG_CENTRALIZED_STATUS_BAR_DIMENS_REFACTOR
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.shared.model.NotificationContainerBounds
import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository
@@ -39,8 +40,11 @@
import com.android.systemui.kosmos.testScope
import com.android.systemui.res.R
import com.android.systemui.shade.data.repository.shadeRepository
+import com.android.systemui.shade.largeScreenHeaderHelper
+import com.android.systemui.shade.mockLargeScreenHeaderHelper
import com.android.systemui.statusbar.notification.stack.domain.interactor.sharedNotificationContainerInteractor
import com.android.systemui.testKosmos
+import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.TestScope
@@ -66,6 +70,7 @@
val keyguardTransitionRepository = kosmos.fakeKeyguardTransitionRepository
val shadeRepository = kosmos.shadeRepository
val sharedNotificationContainerInteractor = kosmos.sharedNotificationContainerInteractor
+ val largeScreenHeaderHelper = kosmos.mockLargeScreenHeaderHelper
val underTest = kosmos.sharedNotificationContainerViewModel
@@ -101,8 +106,10 @@
}
@Test
- fun validatePaddingTopInSplitShade() =
+ fun validatePaddingTopInSplitShade_refactorFlagOff_usesLargeHeaderResource() =
testScope.runTest {
+ mSetFlagsRule.disableFlags(FLAG_CENTRALIZED_STATUS_BAR_DIMENS_REFACTOR)
+ whenever(largeScreenHeaderHelper.getLargeScreenHeaderHeight()).thenReturn(5)
overrideResource(R.bool.config_use_split_notification_shade, true)
overrideResource(R.dimen.large_screen_shade_header_height, 10)
overrideResource(R.dimen.keyguard_split_shade_top_margin, 50)
@@ -115,6 +122,22 @@
}
@Test
+ fun validatePaddingTopInSplitShade_refactorFlagOn_usesLargeHeaderHelper() =
+ testScope.runTest {
+ mSetFlagsRule.enableFlags(FLAG_CENTRALIZED_STATUS_BAR_DIMENS_REFACTOR)
+ whenever(largeScreenHeaderHelper.getLargeScreenHeaderHeight()).thenReturn(5)
+ overrideResource(R.bool.config_use_split_notification_shade, true)
+ overrideResource(R.dimen.large_screen_shade_header_height, 10)
+ overrideResource(R.dimen.keyguard_split_shade_top_margin, 50)
+
+ val dimens by collectLastValue(underTest.configurationBasedDimensions)
+
+ configurationRepository.onAnyConfigurationChange()
+
+ assertThat(dimens!!.paddingTop).isEqualTo(40)
+ }
+
+ @Test
fun validatePaddingTop() =
testScope.runTest {
overrideResource(R.bool.config_use_split_notification_shade, false)
@@ -153,17 +176,41 @@
}
@Test
- fun validateMarginTopWithLargeScreenHeader() =
+ fun validateMarginTopWithLargeScreenHeader_refactorFlagOff_usesResource() =
testScope.runTest {
+ mSetFlagsRule.disableFlags(FLAG_CENTRALIZED_STATUS_BAR_DIMENS_REFACTOR)
+ val headerResourceHeight = 50
+ val headerHelperHeight = 100
+ whenever(largeScreenHeaderHelper.getLargeScreenHeaderHeight())
+ .thenReturn(headerHelperHeight)
overrideResource(R.bool.config_use_large_screen_shade_header, true)
- overrideResource(R.dimen.large_screen_shade_header_height, 50)
+ overrideResource(R.dimen.large_screen_shade_header_height, headerResourceHeight)
overrideResource(R.dimen.notification_panel_margin_top, 0)
val dimens by collectLastValue(underTest.configurationBasedDimensions)
configurationRepository.onAnyConfigurationChange()
- assertThat(dimens!!.marginTop).isEqualTo(50)
+ assertThat(dimens!!.marginTop).isEqualTo(headerResourceHeight)
+ }
+
+ @Test
+ fun validateMarginTopWithLargeScreenHeader_refactorFlagOn_usesHelper() =
+ testScope.runTest {
+ mSetFlagsRule.enableFlags(FLAG_CENTRALIZED_STATUS_BAR_DIMENS_REFACTOR)
+ val headerResourceHeight = 50
+ val headerHelperHeight = 100
+ whenever(largeScreenHeaderHelper.getLargeScreenHeaderHeight())
+ .thenReturn(headerHelperHeight)
+ overrideResource(R.bool.config_use_large_screen_shade_header, true)
+ overrideResource(R.dimen.large_screen_shade_header_height, headerResourceHeight)
+ overrideResource(R.dimen.notification_panel_margin_top, 0)
+
+ val dimens by collectLastValue(underTest.configurationBasedDimensions)
+
+ configurationRepository.onAnyConfigurationChange()
+
+ assertThat(dimens!!.marginTop).isEqualTo(headerHelperHeight)
}
@Test
@@ -275,11 +322,13 @@
}
@Test
- fun boundsOnLockscreenInSplitShade() =
+ fun boundsOnLockscreenInSplitShade_refactorFlagOff_usesLargeHeaderResource() =
testScope.runTest {
+ mSetFlagsRule.disableFlags(FLAG_CENTRALIZED_STATUS_BAR_DIMENS_REFACTOR)
val bounds by collectLastValue(underTest.bounds)
// When in split shade
+ whenever(largeScreenHeaderHelper.getLargeScreenHeaderHeight()).thenReturn(5)
overrideResource(R.bool.config_use_split_notification_shade, true)
overrideResource(R.dimen.large_screen_shade_header_height, 10)
overrideResource(R.dimen.keyguard_split_shade_top_margin, 50)
@@ -300,6 +349,33 @@
}
@Test
+ fun boundsOnLockscreenInSplitShade_refactorFlagOn_usesLargeHeaderHelper() =
+ testScope.runTest {
+ mSetFlagsRule.enableFlags(FLAG_CENTRALIZED_STATUS_BAR_DIMENS_REFACTOR)
+ val bounds by collectLastValue(underTest.bounds)
+
+ // When in split shade
+ whenever(largeScreenHeaderHelper.getLargeScreenHeaderHeight()).thenReturn(5)
+ overrideResource(R.bool.config_use_split_notification_shade, true)
+ overrideResource(R.dimen.large_screen_shade_header_height, 10)
+ overrideResource(R.dimen.keyguard_split_shade_top_margin, 50)
+
+ configurationRepository.onAnyConfigurationChange()
+ runCurrent()
+
+ // Start on lockscreen
+ showLockscreen()
+
+ keyguardInteractor.setNotificationContainerBounds(
+ NotificationContainerBounds(top = 1f, bottom = 2f)
+ )
+ runCurrent()
+
+ // Top should be equal to bounds (1) + padding adjustment (40)
+ assertThat(bounds).isEqualTo(NotificationContainerBounds(top = 41f, bottom = 2f))
+ }
+
+ @Test
fun boundsOnShade() =
testScope.runTest {
val bounds by collectLastValue(underTest.bounds)
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 4dc4798..bbf9a6b 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
@@ -30,11 +30,13 @@
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;
import com.android.systemui.log.core.FakeLogBuffer;
import com.android.systemui.res.R;
+import com.android.systemui.shade.LargeScreenHeaderHelper;
import org.junit.After;
import org.junit.Before;
@@ -79,6 +81,7 @@
MockitoAnnotations.initMocks(this);
mStaticMockSession = mockitoSession()
.mockStatic(BurnInHelperKt.class)
+ .mockStatic(LargeScreenHeaderHelper.class)
.startMocking();
LogBuffer logBuffer = FakeLogBuffer.Factory.Companion.create();
@@ -292,18 +295,44 @@
}
@Test
- public void notifPaddingMakesUpToFullMarginInSplitShade() {
+ public void notifPaddingMakesUpToFullMarginInSplitShade_refactorFlagOff_usesResource() {
+ mSetFlagsRule.disableFlags(Flags.FLAG_CENTRALIZED_STATUS_BAR_DIMENS_REFACTOR);
+ int keyguardSplitShadeTopMargin = 100;
+ int largeScreenHeaderHeightResource = 70;
when(mResources.getDimensionPixelSize(R.dimen.keyguard_split_shade_top_margin))
- .thenReturn(100);
+ .thenReturn(keyguardSplitShadeTopMargin);
when(mResources.getDimensionPixelSize(R.dimen.large_screen_shade_header_height))
- .thenReturn(70);
+ .thenReturn(largeScreenHeaderHeightResource);
mClockPositionAlgorithm.loadDimens(mContext, mResources);
givenLockScreen();
mIsSplitShade = true;
// WHEN the position algorithm is run
positionClock();
- // THEN the notif padding makes up lacking margin (margin - header height = 30).
- assertThat(mClockPosition.stackScrollerPadding).isEqualTo(30);
+ // THEN the notif padding makes up lacking margin (margin - header height).
+ int expectedPadding = keyguardSplitShadeTopMargin - largeScreenHeaderHeightResource;
+ assertThat(mClockPosition.stackScrollerPadding).isEqualTo(expectedPadding);
+ }
+
+ @Test
+ public void notifPaddingMakesUpToFullMarginInSplitShade_refactorFlagOn_usesHelper() {
+ mSetFlagsRule.enableFlags(Flags.FLAG_CENTRALIZED_STATUS_BAR_DIMENS_REFACTOR);
+ int keyguardSplitShadeTopMargin = 100;
+ int largeScreenHeaderHeightHelper = 50;
+ int largeScreenHeaderHeightResource = 70;
+ when(LargeScreenHeaderHelper.getLargeScreenHeaderHeight(mContext))
+ .thenReturn(largeScreenHeaderHeightHelper);
+ when(mResources.getDimensionPixelSize(R.dimen.keyguard_split_shade_top_margin))
+ .thenReturn(keyguardSplitShadeTopMargin);
+ when(mResources.getDimensionPixelSize(R.dimen.large_screen_shade_header_height))
+ .thenReturn(largeScreenHeaderHeightResource);
+ mClockPositionAlgorithm.loadDimens(mContext, mResources);
+ givenLockScreen();
+ mIsSplitShade = true;
+ // WHEN the position algorithm is run
+ positionClock();
+ // THEN the notif padding makes up lacking margin (margin - header height).
+ int expectedPadding = keyguardSplitShadeTopMargin - largeScreenHeaderHeightHelper;
+ assertThat(mClockPosition.stackScrollerPadding).isEqualTo(expectedPadding);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java
index 5102b4f..5fa7f13e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java
@@ -166,7 +166,7 @@
mKeyguardRepository,
mCommandQueue,
PowerInteractorFactory.create().getPowerInteractor(),
- mSceneTestUtils.getSceneContainerFlags(),
+ mSceneTestUtils.getFakeSceneContainerFlags(),
new FakeKeyguardBouncerRepository(),
new ConfigurationInteractor(new FakeConfigurationRepository()),
new FakeShadeRepository(),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/ui/viewmodel/KeyguardStatusBarViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/ui/viewmodel/KeyguardStatusBarViewModelTest.kt
index 9419d63..5f9c096 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/ui/viewmodel/KeyguardStatusBarViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/ui/viewmodel/KeyguardStatusBarViewModelTest.kt
@@ -55,7 +55,7 @@
keyguardRepository,
mock<CommandQueue>(),
PowerInteractorFactory.create().powerInteractor,
- sceneTestUtils.sceneContainerFlags,
+ sceneTestUtils.fakeSceneContainerFlags,
FakeKeyguardBouncerRepository(),
ConfigurationInteractor(FakeConfigurationRepository()),
FakeShadeRepository(),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractorTest.kt
index fb5375a..e6b9d9b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractorTest.kt
@@ -121,10 +121,10 @@
SUPERVISED_USER_CREATION_APP_PACKAGE,
)
- utils.featureFlags.set(Flags.FULL_SCREEN_USER_SWITCHER, false)
+ utils.fakeFeatureFlags.set(Flags.FULL_SCREEN_USER_SWITCHER, false)
mSetFlagsRule.enableFlags(AConfigFlags.FLAG_SWITCH_USER_ON_BG)
spyContext = spy(context)
- keyguardReply = KeyguardInteractorFactory.create(featureFlags = utils.featureFlags)
+ keyguardReply = KeyguardInteractorFactory.create(featureFlags = utils.fakeFeatureFlags)
keyguardRepository = keyguardReply.repository
userRepository = FakeUserRepository()
refreshUsersScheduler =
@@ -363,7 +363,7 @@
fun actions_deviceUnlocked_fullScreen() {
createUserInteractor()
testScope.runTest {
- utils.featureFlags.set(Flags.FULL_SCREEN_USER_SWITCHER, true)
+ utils.fakeFeatureFlags.set(Flags.FULL_SCREEN_USER_SWITCHER, true)
val userInfos = createUserInfos(count = 2, includeGuest = false)
userRepository.setUserInfos(userInfos)
@@ -447,7 +447,7 @@
fun actions_deviceLockedAddFromLockscreenSet_fullList_fullScreen() {
createUserInteractor()
testScope.runTest {
- utils.featureFlags.set(Flags.FULL_SCREEN_USER_SWITCHER, true)
+ utils.fakeFeatureFlags.set(Flags.FULL_SCREEN_USER_SWITCHER, true)
val userInfos = createUserInfos(count = 2, includeGuest = false)
userRepository.setUserInfos(userInfos)
userRepository.setSelectedUserInfo(userInfos[0])
@@ -792,7 +792,7 @@
fun userRecordsFullScreen() {
createUserInteractor()
testScope.runTest {
- utils.featureFlags.set(Flags.FULL_SCREEN_USER_SWITCHER, true)
+ utils.fakeFeatureFlags.set(Flags.FULL_SCREEN_USER_SWITCHER, true)
val userInfos = createUserInfos(count = 3, includeGuest = false)
userRepository.setSettings(UserSwitcherSettingsModel(isUserSwitcherEnabled = true))
userRepository.setUserInfos(userInfos)
@@ -901,7 +901,7 @@
fun showUserSwitcher_fullScreenEnabled_launchesFullScreenDialog() {
createUserInteractor()
testScope.runTest {
- utils.featureFlags.set(Flags.FULL_SCREEN_USER_SWITCHER, true)
+ utils.fakeFeatureFlags.set(Flags.FULL_SCREEN_USER_SWITCHER, true)
val expandable = mock<Expandable>()
underTest.showUserSwitcher(expandable)
@@ -1139,7 +1139,7 @@
resetOrExitSessionReceiver = resetOrExitSessionReceiver,
),
uiEventLogger = uiEventLogger,
- featureFlags = utils.featureFlags,
+ featureFlags = utils.fakeFeatureFlags,
userRestrictionChecker = mock(),
)
}
diff --git a/packages/SystemUI/tests/utils/src/android/content/ContextKosmos.kt b/packages/SystemUI/tests/utils/src/android/content/ContextKosmos.kt
index f96c508..5e254bf 100644
--- a/packages/SystemUI/tests/utils/src/android/content/ContextKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/android/content/ContextKosmos.kt
@@ -16,9 +16,7 @@
package android.content
-import com.android.systemui.SysuiTestableContext
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testCase
-val Kosmos.testableContext: SysuiTestableContext by Kosmos.Fixture { testCase.context }
-var Kosmos.applicationContext: Context by Kosmos.Fixture { testableContext }
+var Kosmos.applicationContext: Context by Kosmos.Fixture { testCase.context }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotifCollectionKosmos.kt b/packages/SystemUI/tests/utils/src/android/service/notification/NotificationListenerServiceKosmos.kt
similarity index 77%
rename from packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotifCollectionKosmos.kt
rename to packages/SystemUI/tests/utils/src/android/service/notification/NotificationListenerServiceKosmos.kt
index d98f496..bff0d0e 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotifCollectionKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/android/service/notification/NotificationListenerServiceKosmos.kt
@@ -14,11 +14,11 @@
* limitations under the License.
*/
-package com.android.systemui.statusbar.notification.stack.ui.viewbinder
+package android.service.notification
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
-import com.android.systemui.statusbar.notification.collection.NotifCollection
import com.android.systemui.util.mockito.mock
-var Kosmos.notifCollection by Fixture { mock<NotifCollection>() }
+val Kosmos.notificationListenerService by Fixture { mockNotificationListenerService }
+val Kosmos.mockNotificationListenerService by Fixture { mock<NotificationListenerService>() }
diff --git a/packages/SystemUI/tests/utils/src/android/telephony/TelephonyManagerKosmos.kt b/packages/SystemUI/tests/utils/src/android/telephony/TelephonyManagerKosmos.kt
new file mode 100644
index 0000000..a4ee702
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/android/telephony/TelephonyManagerKosmos.kt
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.Kosmos.Fixture
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.whenever
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.ArgumentMatchers.anyString
+
+val Kosmos.telephonyManager by Fixture {
+ mock<TelephonyManager> {
+ whenever(createForSubscriptionId(anyInt())).thenReturn(this)
+ whenever(supplyIccLockPin(anyString()))
+ .thenReturn(PinResult(PinResult.PIN_RESULT_TYPE_SUCCESS, 3))
+ }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotifCollectionKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/app/ActivityTaskManagerKosmos.kt
similarity index 71%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotifCollectionKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/app/ActivityTaskManagerKosmos.kt
index d98f496..fa3e8f9 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotifCollectionKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/app/ActivityTaskManagerKosmos.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,11 +14,11 @@
* limitations under the License.
*/
-package com.android.systemui.statusbar.notification.stack.ui.viewbinder
+package com.android.app
+import android.app.ActivityTaskManager
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
-import com.android.systemui.statusbar.notification.collection.NotifCollection
import com.android.systemui.util.mockito.mock
-var Kosmos.notifCollection by Fixture { mock<NotifCollection>() }
+val Kosmos.activityTaskManager by Fixture { mock<ActivityTaskManager>() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/internal/logging/MetricsLoggerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/internal/logging/MetricsLoggerKosmos.kt
index a1815c5..eedc0b9 100644
--- a/packages/SystemUI/tests/utils/src/com/android/internal/logging/MetricsLoggerKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/internal/logging/MetricsLoggerKosmos.kt
@@ -16,8 +16,9 @@
package com.android.internal.logging
+import com.android.internal.logging.testing.FakeMetricsLogger
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
-import com.android.systemui.util.mockito.mock
-var Kosmos.metricsLogger by Fixture { mock<MetricsLogger>() }
+val Kosmos.fakeMetricsLogger by Fixture { FakeMetricsLogger() }
+val Kosmos.metricsLogger by Fixture<MetricsLogger> { fakeMetricsLogger }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotifCollectionKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/internal/util/EmergencyAffordanceManagerKosmos.kt
similarity index 71%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotifCollectionKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/internal/util/EmergencyAffordanceManagerKosmos.kt
index d98f496..3133437 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotifCollectionKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/internal/util/EmergencyAffordanceManagerKosmos.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,11 +14,10 @@
* limitations under the License.
*/
-package com.android.systemui.statusbar.notification.stack.ui.viewbinder
+package com.android.internal.util
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
-import com.android.systemui.statusbar.notification.collection.NotifCollection
import com.android.systemui.util.mockito.mock
-var Kosmos.notifCollection by Fixture { mock<NotifCollection>() }
+val Kosmos.emergencyAffordanceManager by Fixture { mock<EmergencyAffordanceManager>() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java b/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java
index d23dae9..af7f4c8 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java
@@ -39,6 +39,7 @@
import androidx.test.uiautomator.UiDevice;
import com.android.systemui.broadcast.FakeBroadcastDispatcher;
+import com.android.systemui.flags.SceneContainerRule;
import org.junit.After;
import org.junit.AfterClass;
@@ -68,6 +69,9 @@
@Rule
public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(DEVICE_DEFAULT);
+ @Rule(order = 10)
+ public final SceneContainerRule mSceneContainerRule = new SceneContainerRule();
+
@Rule
public SysuiTestableContext mContext = createTestableContext();
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/TestMocksModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/TestMocksModule.kt
index fdb9b30..b18859d 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/TestMocksModule.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/TestMocksModule.kt
@@ -18,9 +18,11 @@
import android.app.ActivityManager
import android.app.admin.DevicePolicyManager
import android.os.UserManager
+import android.service.notification.NotificationListenerService
import android.util.DisplayMetrics
import android.view.LayoutInflater
import com.android.internal.logging.MetricsLogger
+import com.android.internal.statusbar.IStatusBarService
import com.android.keyguard.KeyguardSecurityModel
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.keyguard.KeyguardViewController
@@ -49,9 +51,11 @@
import com.android.systemui.statusbar.SysuiStatusBarStateController
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator
import com.android.systemui.statusbar.notification.collection.NotifCollection
+import com.android.systemui.statusbar.notification.logging.NotificationPanelLogger
import com.android.systemui.statusbar.notification.stack.AmbientState
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
import com.android.systemui.statusbar.notification.stack.NotificationStackSizeCalculator
+import com.android.systemui.statusbar.notification.stack.ui.view.NotificationStatsLogger
import com.android.systemui.statusbar.phone.DozeParameters
import com.android.systemui.statusbar.phone.KeyguardBypassController
import com.android.systemui.statusbar.phone.LSShadeTransitionLogger
@@ -59,6 +63,7 @@
import com.android.systemui.statusbar.phone.ScrimController
import com.android.systemui.statusbar.phone.SystemUIDialogManager
import com.android.systemui.statusbar.policy.DeviceProvisionedController
+import com.android.systemui.statusbar.policy.HeadsUpManager
import com.android.systemui.statusbar.policy.ZenModeController
import com.android.systemui.statusbar.window.StatusBarWindowController
import com.android.systemui.unfold.UnfoldTransitionProgressProvider
@@ -80,6 +85,7 @@
@get:Provides val deviceProvisionedController: DeviceProvisionedController = mock(),
@get:Provides val dozeParameters: DozeParameters = mock(),
@get:Provides val dumpManager: DumpManager = mock(),
+ @get:Provides val headsUpManager: HeadsUpManager = mock(),
@get:Provides val guestResumeSessionReceiver: GuestResumeSessionReceiver = mock(),
@get:Provides val keyguardBypassController: KeyguardBypassController = mock(),
@get:Provides val keyguardSecurityModel: KeyguardSecurityModel = mock(),
@@ -89,8 +95,10 @@
val lockscreenShadeTransitionController: LockscreenShadeTransitionController = mock(),
@get:Provides val mediaHierarchyManager: MediaHierarchyManager = mock(),
@get:Provides val notifCollection: NotifCollection = mock(),
+ @get:Provides val notificationListLogger: NotificationStatsLogger = mock(),
@get:Provides val notificationListener: NotificationListener = mock(),
@get:Provides val notificationLockscreenUserManager: NotificationLockscreenUserManager = mock(),
+ @get:Provides val notificationPanelLogger: NotificationPanelLogger = mock(),
@get:Provides val notificationMediaManager: NotificationMediaManager = mock(),
@get:Provides val notificationShadeDepthController: NotificationShadeDepthController = mock(),
@get:Provides
@@ -129,6 +137,10 @@
@get:Provides val displayMetrics: DisplayMetrics = mock(),
@get:Provides val metricsLogger: MetricsLogger = mock(),
@get:Provides val userManager: UserManager = mock(),
+
+ // system server mocks
+ @get:Provides val mockStatusBarService: IStatusBarService = mock(),
+ @get:Provides val mockNotificationListenerService: NotificationListenerService = mock(),
) {
@Module
interface Bindings {
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotifCollectionKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/data/repository/BouncerRepositoryKosmos.kt
similarity index 67%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotifCollectionKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/data/repository/BouncerRepositoryKosmos.kt
index d98f496..c0f8638 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotifCollectionKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/data/repository/BouncerRepositoryKosmos.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,11 +14,14 @@
* limitations under the License.
*/
-package com.android.systemui.statusbar.notification.stack.ui.viewbinder
+package com.android.systemui.bouncer.data.repository
+import com.android.systemui.flags.featureFlagsClassic
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
-import com.android.systemui.statusbar.notification.collection.NotifCollection
-import com.android.systemui.util.mockito.mock
-var Kosmos.notifCollection by Fixture { mock<NotifCollection>() }
+val Kosmos.bouncerRepository by Fixture {
+ BouncerRepository(
+ flags = featureFlagsClassic,
+ )
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/data/repository/EmergencyServicesRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/data/repository/EmergencyServicesRepositoryKosmos.kt
new file mode 100644
index 0000000..8851709
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/data/repository/EmergencyServicesRepositoryKosmos.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.bouncer.data.repository
+
+import android.content.res.mainResources
+import com.android.systemui.common.ui.data.repository.configurationRepository
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.Kosmos.Fixture
+import com.android.systemui.kosmos.testScope
+
+val Kosmos.emergencyServicesRepository by Fixture {
+ EmergencyServicesRepository(
+ applicationScope = testScope.backgroundScope,
+ resources = mainResources,
+ configurationRepository = configurationRepository,
+ )
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotifCollectionKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/data/repository/SimBouncerRepositoryKosmos.kt
similarity index 67%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotifCollectionKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/data/repository/SimBouncerRepositoryKosmos.kt
index d98f496..7af39df 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotifCollectionKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/data/repository/SimBouncerRepositoryKosmos.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,11 +14,11 @@
* limitations under the License.
*/
-package com.android.systemui.statusbar.notification.stack.ui.viewbinder
+package com.android.systemui.bouncer.data.repository
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
-import com.android.systemui.statusbar.notification.collection.NotifCollection
-import com.android.systemui.util.mockito.mock
-var Kosmos.notifCollection by Fixture { mock<NotifCollection>() }
+val Kosmos.fakeSimBouncerRepository by Fixture { FakeSimBouncerRepository() }
+
+val Kosmos.simBouncerRepository by Fixture<SimBouncerRepository> { fakeSimBouncerRepository }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorKosmos.kt
index 86a4509..c4fc30d 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorKosmos.kt
@@ -25,7 +25,7 @@
import com.android.systemui.plugins.statusbar.statusBarStateController
import com.android.systemui.statusbar.policy.KeyguardStateControllerImpl
import com.android.systemui.util.mockito.mock
-import com.android.systemui.util.time.fakeSystemClock
+import com.android.systemui.util.time.systemClock
var Kosmos.alternateBouncerInteractor by
Kosmos.Fixture {
@@ -35,7 +35,7 @@
bouncerRepository = keyguardBouncerRepository,
fingerprintPropertyRepository = fingerprintPropertyRepository,
biometricSettingsRepository = biometricSettingsRepository,
- systemClock = fakeSystemClock,
+ systemClock = systemClock,
keyguardUpdateMonitor = keyguardUpdateMonitor,
scope = testScope.backgroundScope,
)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/domain/interactor/BouncerActionButtonInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/domain/interactor/BouncerActionButtonInteractorKosmos.kt
new file mode 100644
index 0000000..5ced578
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/domain/interactor/BouncerActionButtonInteractorKosmos.kt
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.bouncer.domain.interactor
+
+import android.content.Intent
+import android.content.applicationContext
+import com.android.app.activityTaskManager
+import com.android.internal.logging.metricsLogger
+import com.android.internal.util.emergencyAffordanceManager
+import com.android.systemui.authentication.domain.interactor.authenticationInteractor
+import com.android.systemui.bouncer.data.repository.emergencyServicesRepository
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.Kosmos.Fixture
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.mobileConnectionsRepository
+import com.android.systemui.telephony.domain.interactor.telephonyInteractor
+import com.android.systemui.user.domain.interactor.selectedUserInteractor
+import com.android.systemui.util.mockito.mock
+import com.android.telecom.telecomManager
+
+val Kosmos.bouncerActionButtonInteractor by Fixture {
+ BouncerActionButtonInteractor(
+ applicationContext = applicationContext,
+ backgroundDispatcher = testDispatcher,
+ repository = emergencyServicesRepository,
+ mobileConnectionsRepository = mobileConnectionsRepository,
+ telephonyInteractor = telephonyInteractor,
+ authenticationInteractor = authenticationInteractor,
+ selectedUserInteractor = selectedUserInteractor,
+ activityTaskManager = activityTaskManager,
+ telecomManager = telecomManager,
+ emergencyAffordanceManager = emergencyAffordanceManager,
+ emergencyDialerIntentFactory =
+ object : EmergencyDialerIntentFactory {
+ override fun invoke(): Intent = Intent()
+ },
+ metricsLogger = metricsLogger,
+ dozeLogger = mock(),
+ )
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorKosmos.kt
new file mode 100644
index 0000000..27803b2
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorKosmos.kt
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.bouncer.domain.interactor
+
+import android.content.applicationContext
+import com.android.systemui.authentication.domain.interactor.authenticationInteractor
+import com.android.systemui.bouncer.data.repository.bouncerRepository
+import com.android.systemui.classifier.domain.interactor.falsingInteractor
+import com.android.systemui.deviceentry.domain.interactor.deviceEntryFaceAuthInteractor
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.Kosmos.Fixture
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.power.domain.interactor.powerInteractor
+
+val Kosmos.bouncerInteractor by Fixture {
+ BouncerInteractor(
+ applicationScope = testScope.backgroundScope,
+ applicationContext = applicationContext,
+ repository = bouncerRepository,
+ authenticationInteractor = authenticationInteractor,
+ deviceEntryFaceAuthInteractor = deviceEntryFaceAuthInteractor,
+ falsingInteractor = falsingInteractor,
+ powerInteractor = powerInteractor,
+ simBouncerInteractor = simBouncerInteractor,
+ )
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/domain/interactor/SimBouncerInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/domain/interactor/SimBouncerInteractorKosmos.kt
new file mode 100644
index 0000000..8ed9f45
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/domain/interactor/SimBouncerInteractorKosmos.kt
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.bouncer.domain.interactor
+
+import android.content.Context
+import android.content.applicationContext
+import android.content.res.mainResources
+import android.telephony.euicc.EuiccManager
+import android.telephony.telephonyManager
+import com.android.keyguard.keyguardUpdateMonitor
+import com.android.systemui.bouncer.data.repository.simBouncerRepository
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.Kosmos.Fixture
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.mobileConnectionsRepository
+
+val Kosmos.simBouncerInteractor by Fixture {
+ SimBouncerInteractor(
+ applicationContext = applicationContext,
+ backgroundDispatcher = testDispatcher,
+ applicationScope = testScope.backgroundScope,
+ repository = simBouncerRepository,
+ telephonyManager = telephonyManager,
+ resources = mainResources,
+ keyguardUpdateMonitor = keyguardUpdateMonitor,
+ euiccManager = applicationContext.getSystemService(Context.EUICC_SERVICE) as EuiccManager,
+ mobileConnectionsRepository = mobileConnectionsRepository,
+ )
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelKosmos.kt
new file mode 100644
index 0000000..d91c597
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelKosmos.kt
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.bouncer.ui.viewmodel
+
+import android.content.applicationContext
+import com.android.systemui.authentication.domain.interactor.authenticationInteractor
+import com.android.systemui.bouncer.domain.interactor.bouncerActionButtonInteractor
+import com.android.systemui.bouncer.domain.interactor.bouncerInteractor
+import com.android.systemui.bouncer.domain.interactor.simBouncerInteractor
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.Kosmos.Fixture
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.scene.shared.flag.sceneContainerFlags
+import com.android.systemui.user.ui.viewmodel.userSwitcherViewModel
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.time.systemClock
+
+val Kosmos.bouncerViewModel by Fixture {
+ BouncerViewModel(
+ applicationContext = applicationContext,
+ applicationScope = testScope.backgroundScope,
+ mainDispatcher = testDispatcher,
+ bouncerInteractor = bouncerInteractor,
+ simBouncerInteractor = simBouncerInteractor,
+ authenticationInteractor = authenticationInteractor,
+ flags = sceneContainerFlags,
+ selectedUser = userSwitcherViewModel.selectedUser,
+ users = userSwitcherViewModel.users,
+ userSwitcherMenu = userSwitcherViewModel.menu,
+ actionButton = bouncerActionButtonInteractor.actionButton,
+ clock = systemClock,
+ devicePolicyManager = mock(),
+ )
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotifCollectionKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/classifier/domain/interactor/FalsingInteractorKosmos.kt
similarity index 67%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotifCollectionKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/classifier/domain/interactor/FalsingInteractorKosmos.kt
index d98f496..8fee5b2 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotifCollectionKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/classifier/domain/interactor/FalsingInteractorKosmos.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,11 +14,14 @@
* limitations under the License.
*/
-package com.android.systemui.statusbar.notification.stack.ui.viewbinder
+package com.android.systemui.classifier.domain.interactor
+import com.android.systemui.classifier.falsingCollector
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
-import com.android.systemui.statusbar.notification.collection.NotifCollection
-import com.android.systemui.util.mockito.mock
-var Kosmos.notifCollection by Fixture { mock<NotifCollection>() }
+val Kosmos.falsingInteractor by Fixture {
+ FalsingInteractor(
+ collector = falsingCollector,
+ )
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/CommunalMediaRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/CommunalMediaRepositoryKosmos.kt
index 7946446..0768106 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/CommunalMediaRepositoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/CommunalMediaRepositoryKosmos.kt
@@ -18,6 +18,7 @@
import com.android.systemui.kosmos.Kosmos
-var Kosmos.communalMediaRepository: CommunalMediaRepository by
- Kosmos.Fixture { fakeCommunalMediaRepository }
val Kosmos.fakeCommunalMediaRepository by Kosmos.Fixture { FakeCommunalMediaRepository() }
+
+val Kosmos.communalMediaRepository by
+ Kosmos.Fixture<CommunalMediaRepository> { fakeCommunalMediaRepository }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/CommunalRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/CommunalRepositoryKosmos.kt
index be56d2b..1f5af5c 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/CommunalRepositoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/CommunalRepositoryKosmos.kt
@@ -17,6 +17,8 @@
package com.android.systemui.communal.data.repository
import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.Kosmos.Fixture
-var Kosmos.communalRepository: CommunalRepository by Kosmos.Fixture { fakeCommunalRepository }
-val Kosmos.fakeCommunalRepository by Kosmos.Fixture { FakeCommunalRepository() }
+val Kosmos.fakeCommunalRepository by Fixture { FakeCommunalRepository() }
+
+val Kosmos.communalRepository by Fixture<CommunalRepository> { fakeCommunalRepository }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryKosmos.kt
index 5a17f2f8..c225e3c 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryKosmos.kt
@@ -17,9 +17,14 @@
package com.android.systemui.communal.data.repository
import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.Kosmos.Fixture
import com.android.systemui.kosmos.applicationCoroutineScope
-var Kosmos.communalWidgetRepository: CommunalWidgetRepository by
- Kosmos.Fixture { fakeCommunalWidgetRepository }
-val Kosmos.fakeCommunalWidgetRepository by
- Kosmos.Fixture { FakeCommunalWidgetRepository(applicationCoroutineScope) }
+val Kosmos.fakeCommunalWidgetRepository by Fixture {
+ FakeCommunalWidgetRepository(
+ coroutineScope = applicationCoroutineScope,
+ )
+}
+
+val Kosmos.communalWidgetRepository by
+ Fixture<CommunalWidgetRepository> { fakeCommunalWidgetRepository }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorKosmos.kt
new file mode 100644
index 0000000..649b373
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorKosmos.kt
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.communal.domain.interactor
+
+import com.android.systemui.communal.data.repository.communalMediaRepository
+import com.android.systemui.communal.data.repository.communalRepository
+import com.android.systemui.communal.data.repository.communalWidgetRepository
+import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.Kosmos.Fixture
+import com.android.systemui.smartspace.data.repository.smartspaceRepository
+import com.android.systemui.util.mockito.mock
+
+val Kosmos.communalInteractor by Fixture {
+ CommunalInteractor(
+ communalRepository = communalRepository,
+ widgetRepository = communalWidgetRepository,
+ mediaRepository = communalMediaRepository,
+ smartspaceRepository = smartspaceRepository,
+ appWidgetHost = mock(),
+ keyguardInteractor = keyguardInteractor,
+ editWidgetsActivityStarter = mock(),
+ )
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryHapticsInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryHapticsInteractorKosmos.kt
index 6bf527d..de58ae5 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryHapticsInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryHapticsInteractorKosmos.kt
@@ -24,7 +24,7 @@
import com.android.systemui.keyguard.data.repository.biometricSettingsRepository
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.power.domain.interactor.powerInteractor
-import com.android.systemui.util.time.fakeSystemClock
+import com.android.systemui.util.time.systemClock
import kotlinx.coroutines.ExperimentalCoroutinesApi
val Kosmos.deviceEntryHapticsInteractor by
@@ -37,7 +37,7 @@
biometricSettingsRepository = biometricSettingsRepository,
keyEventInteractor = keyEventInteractor,
powerInteractor = powerInteractor,
- systemClock = fakeSystemClock,
+ systemClock = systemClock,
logger = biometricUnlockLogger,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/EnableSceneContainer.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/flags/EnableSceneContainer.kt
new file mode 100644
index 0000000..97f84c6
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/flags/EnableSceneContainer.kt
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.flags
+
+import android.platform.test.annotations.EnableFlags
+import com.android.systemui.Flags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR
+import com.android.systemui.Flags.FLAG_KEYGUARD_SHADE_MIGRATION_NSSL
+import com.android.systemui.Flags.FLAG_MEDIA_IN_SCENE_CONTAINER
+import com.android.systemui.Flags.FLAG_SCENE_CONTAINER
+
+/**
+ * This includes @[EnableFlags] to work with [SetFlagsRule] to enable all aconfig flags required by
+ * that feature. It is also picked up by [SceneContainerRule] to set non-aconfig prerequisites.
+ */
+@EnableFlags(
+ FLAG_SCENE_CONTAINER,
+ FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR,
+ FLAG_KEYGUARD_SHADE_MIGRATION_NSSL,
+ FLAG_MEDIA_IN_SCENE_CONTAINER,
+)
+@Retention(AnnotationRetention.RUNTIME)
+@Target(AnnotationTarget.FUNCTION, AnnotationTarget.CLASS)
+annotation class EnableSceneContainer
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsClassicKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsClassicKosmos.kt
index abadaf7..7b36a29 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsClassicKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsClassicKosmos.kt
@@ -30,7 +30,13 @@
* Fixture supplying a shared [FakeFeatureFlagsClassic] instance. Can be accessed in tests in order
* to override flag values.
*/
-val Kosmos.fakeFeatureFlagsClassic by Kosmos.Fixture { FakeFeatureFlagsClassic() }
+val Kosmos.fakeFeatureFlagsClassic by
+ Kosmos.Fixture {
+ FakeFeatureFlagsClassic().apply {
+ set(Flags.FULL_SCREEN_USER_SWITCHER, false)
+ set(Flags.NSSL_DEBUG_LINES, false)
+ }
+ }
/**
* Fixture supplying a real [FeatureFlagsClassicRelease] instance, for use by tests that want to
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/SceneContainerRule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/flags/SceneContainerRule.kt
new file mode 100644
index 0000000..3faa6eb
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/flags/SceneContainerRule.kt
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.flags
+
+import android.util.Log
+import com.android.systemui.compose.ComposeFacade
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
+import org.junit.Assert
+import org.junit.Assume
+import org.junit.rules.TestRule
+import org.junit.runner.Description
+import org.junit.runners.model.Statement
+
+/**
+ * Should always be used with [SetFlagsRule] and should be ordered after it.
+ *
+ * Used to ensure tests annotated with [EnableSceneContainer] can actually get `true` from
+ * [SceneContainerFlag.isEnabled].
+ */
+class SceneContainerRule : TestRule {
+ override fun apply(base: Statement?, description: Description?): Statement {
+ return object : Statement() {
+ @Throws(Throwable::class)
+ override fun evaluate() {
+ val initialEnabledValue = Flags.SCENE_CONTAINER_ENABLED
+ val hasAnnotation =
+ description?.testClass?.getAnnotation(EnableSceneContainer::class.java) !=
+ null || description?.getAnnotation(EnableSceneContainer::class.java) != null
+ if (hasAnnotation) {
+ Assume.assumeTrue(
+ "Compose must be available for @EnableSceneContainer test",
+ ComposeFacade.isComposeAvailable()
+ )
+ Assume.assumeTrue(
+ "Couldn't set Flags.SCENE_CONTAINER_ENABLED for @EnableSceneContainer test",
+ trySetSceneContainerEnabled(true)
+ )
+ Assert.assertTrue(
+ "SceneContainerFlag.isEnabled is false:" +
+ "\n * Did you forget to add a new aconfig flag dependency in" +
+ " @EnableSceneContainer?" +
+ "\n * Did you forget to use SetFlagsRule with an earlier order?",
+ SceneContainerFlag.isEnabled
+ )
+ }
+ try {
+ base?.evaluate()
+ } finally {
+ if (hasAnnotation) {
+ trySetSceneContainerEnabled(initialEnabledValue)
+ }
+ }
+ }
+ }
+ }
+
+ companion object {
+ fun trySetSceneContainerEnabled(enabled: Boolean): Boolean {
+ if (Flags.SCENE_CONTAINER_ENABLED == enabled) {
+ return true
+ }
+ return try {
+ // TODO(b/283300105): remove this reflection setting once the hard-coded
+ // Flags.SCENE_CONTAINER_ENABLED is no longer needed.
+ val field = Flags::class.java.getField("SCENE_CONTAINER_ENABLED")
+ field.isAccessible = true
+ field.set(null, enabled) // note: this does not work with multivalent tests
+ true
+ } catch (t: Throwable) {
+ Log.e("SceneContainerRule", "Unable to set SCENE_CONTAINER_ENABLED=$enabled", t)
+ false
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotifCollectionKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/jank/InteractionJankMonitorKosmos.kt
similarity index 71%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotifCollectionKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/jank/InteractionJankMonitorKosmos.kt
index d98f496..5c5016d 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotifCollectionKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/jank/InteractionJankMonitorKosmos.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,11 +14,11 @@
* limitations under the License.
*/
-package com.android.systemui.statusbar.notification.stack.ui.viewbinder
+package com.android.systemui.jank
+import com.android.internal.jank.InteractionJankMonitor
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
-import com.android.systemui.statusbar.notification.collection.NotifCollection
import com.android.systemui.util.mockito.mock
-var Kosmos.notifCollection by Fixture { mock<NotifCollection>() }
+val Kosmos.interactionJankMonitor by Fixture<InteractionJankMonitor> { mock() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/plugins/statusbar/StatusBarStateControllerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/plugins/statusbar/StatusBarStateControllerKosmos.kt
index cac2646..73b7c50 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/plugins/statusbar/StatusBarStateControllerKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/plugins/statusbar/StatusBarStateControllerKosmos.kt
@@ -16,7 +16,20 @@
package com.android.systemui.plugins.statusbar
+import com.android.systemui.jank.interactionJankMonitor
import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.qs.uiEventLogger
+import com.android.systemui.shade.domain.interactor.shadeInteractor
+import com.android.systemui.statusbar.StatusBarStateControllerImpl
import com.android.systemui.util.mockito.mock
-var Kosmos.statusBarStateController by Kosmos.Fixture { mock<StatusBarStateController>() }
+var Kosmos.statusBarStateController by
+ Kosmos.Fixture {
+ StatusBarStateControllerImpl(
+ uiEventLogger,
+ interactionJankMonitor,
+ mock(),
+ ) {
+ shadeInteractor
+ }
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt
index 09ab655..d314a25 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt
@@ -16,182 +16,108 @@
package com.android.systemui.scene
-import android.app.ActivityTaskManager
-import android.app.admin.DevicePolicyManager
import android.content.Context
-import android.content.Intent
-import android.content.pm.UserInfo
-import android.graphics.Bitmap
-import android.graphics.drawable.BitmapDrawable
-import android.telecom.TelecomManager
-import android.telephony.PinResult
-import android.telephony.PinResult.PIN_RESULT_TYPE_SUCCESS
-import android.telephony.TelephonyManager
-import android.telephony.euicc.EuiccManager
-import com.android.internal.logging.MetricsLogger
-import com.android.internal.util.EmergencyAffordanceManager
+import android.content.applicationContext
import com.android.systemui.SysuiTestCase
-import com.android.systemui.authentication.data.repository.AuthenticationRepository
-import com.android.systemui.authentication.data.repository.FakeAuthenticationRepository
+import com.android.systemui.authentication.data.repository.fakeAuthenticationRepository
import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor
-import com.android.systemui.bouncer.data.repository.BouncerRepository
-import com.android.systemui.bouncer.data.repository.EmergencyServicesRepository
-import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository
-import com.android.systemui.bouncer.data.repository.FakeSimBouncerRepository
+import com.android.systemui.authentication.domain.interactor.authenticationInteractor
+import com.android.systemui.bouncer.data.repository.bouncerRepository
+import com.android.systemui.bouncer.data.repository.fakeSimBouncerRepository
import com.android.systemui.bouncer.domain.interactor.BouncerActionButtonInteractor
import com.android.systemui.bouncer.domain.interactor.BouncerInteractor
-import com.android.systemui.bouncer.domain.interactor.EmergencyDialerIntentFactory
-import com.android.systemui.bouncer.domain.interactor.SimBouncerInteractor
+import com.android.systemui.bouncer.domain.interactor.bouncerActionButtonInteractor
+import com.android.systemui.bouncer.domain.interactor.bouncerInteractor
+import com.android.systemui.bouncer.domain.interactor.simBouncerInteractor
import com.android.systemui.bouncer.ui.viewmodel.BouncerViewModel
+import com.android.systemui.bouncer.ui.viewmodel.bouncerViewModel
import com.android.systemui.classifier.FalsingCollector
-import com.android.systemui.classifier.FalsingCollectorFake
import com.android.systemui.classifier.domain.interactor.FalsingInteractor
-import com.android.systemui.common.shared.model.Text
-import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository
-import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
-import com.android.systemui.communal.data.repository.FakeCommunalRepository
+import com.android.systemui.classifier.domain.interactor.falsingInteractor
+import com.android.systemui.classifier.falsingCollector
+import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository
+import com.android.systemui.common.ui.domain.interactor.configurationInteractor
+import com.android.systemui.communal.data.repository.fakeCommunalRepository
import com.android.systemui.communal.domain.interactor.CommunalInteractor
-import com.android.systemui.communal.domain.interactor.CommunalInteractorFactory
-import com.android.systemui.deviceentry.data.repository.DeviceEntryFaceAuthRepository
-import com.android.systemui.deviceentry.data.repository.DeviceEntryRepository
-import com.android.systemui.deviceentry.data.repository.FakeDeviceEntryRepository
-import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor
+import com.android.systemui.communal.domain.interactor.communalInteractor
+import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryRepository
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor
-import com.android.systemui.doze.DozeLogger
-import com.android.systemui.flags.FakeFeatureFlagsClassic
-import com.android.systemui.flags.Flags
-import com.android.systemui.keyguard.data.repository.FakeCommandQueue
-import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFaceAuthRepository
-import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
-import com.android.systemui.keyguard.data.repository.FakeTrustRepository
-import com.android.systemui.keyguard.data.repository.KeyguardRepository
-import com.android.systemui.keyguard.data.repository.TrustRepository
+import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor
+import com.android.systemui.flags.fakeFeatureFlagsClassic
+import com.android.systemui.jank.interactionJankMonitor
+import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
-import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel
+import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testCase
import com.android.systemui.kosmos.testDispatcher
import com.android.systemui.kosmos.testScope
-import com.android.systemui.plugins.statusbar.StatusBarStateController
-import com.android.systemui.power.data.repository.FakePowerRepository
-import com.android.systemui.power.data.repository.PowerRepository
+import com.android.systemui.plugins.statusbar.statusBarStateController
+import com.android.systemui.power.data.repository.fakePowerRepository
import com.android.systemui.power.domain.interactor.PowerInteractor
-import com.android.systemui.power.domain.interactor.PowerInteractorFactory
+import com.android.systemui.power.domain.interactor.powerInteractor
import com.android.systemui.scene.data.repository.SceneContainerRepository
+import com.android.systemui.scene.data.repository.sceneContainerRepository
import com.android.systemui.scene.domain.interactor.SceneInteractor
-import com.android.systemui.scene.shared.flag.FakeSceneContainerFlags
+import com.android.systemui.scene.domain.interactor.sceneInteractor
+import com.android.systemui.scene.shared.flag.fakeSceneContainerFlags
import com.android.systemui.scene.shared.model.SceneContainerConfig
import com.android.systemui.scene.shared.model.SceneKey
-import com.android.systemui.shade.data.repository.FakeShadeRepository
-import com.android.systemui.statusbar.notification.stack.data.repository.NotificationStackAppearanceRepository
-import com.android.systemui.statusbar.notification.stack.domain.interactor.NotificationStackAppearanceInteractor
import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationsPlaceholderViewModel
-import com.android.systemui.statusbar.phone.ScreenOffAnimationController
-import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionsRepository
-import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionsRepository
-import com.android.systemui.telephony.data.repository.FakeTelephonyRepository
-import com.android.systemui.telephony.data.repository.TelephonyRepository
+import com.android.systemui.statusbar.notification.stack.ui.viewmodel.notificationsPlaceholderViewModel
+import com.android.systemui.statusbar.phone.screenOffAnimationController
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.fakeMobileConnectionsRepository
+import com.android.systemui.telephony.data.repository.fakeTelephonyRepository
import com.android.systemui.telephony.domain.interactor.TelephonyInteractor
-import com.android.systemui.user.data.repository.FakeUserRepository
+import com.android.systemui.telephony.domain.interactor.telephonyInteractor
import com.android.systemui.user.domain.interactor.SelectedUserInteractor
-import com.android.systemui.user.ui.viewmodel.UserActionViewModel
-import com.android.systemui.user.ui.viewmodel.UserViewModel
-import com.android.systemui.util.mockito.mock
-import com.android.systemui.util.mockito.whenever
-import com.android.systemui.util.time.SystemClock
-import kotlinx.coroutines.CoroutineScope
+import com.android.systemui.user.domain.interactor.selectedUserInteractor
+import com.android.systemui.util.time.systemClock
import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.flow.flowOf
-import kotlinx.coroutines.runBlocking
-import kotlinx.coroutines.test.currentTime
-import org.mockito.ArgumentMatchers.anyInt
-import org.mockito.ArgumentMatchers.anyString
/**
* Utilities for creating scene container framework related repositories, interactors, and
* view-models for tests.
*/
@OptIn(ExperimentalCoroutinesApi::class)
-class SceneTestUtils(
- private val context: Context,
-) {
- constructor(test: SysuiTestCase) : this(context = test.context)
+@Deprecated("Please use Kosmos instead.")
+class SceneTestUtils {
val kosmos = Kosmos()
- val testDispatcher = kosmos.testDispatcher
- val testScope = kosmos.testScope
- val featureFlags =
- FakeFeatureFlagsClassic().apply {
- set(Flags.FULL_SCREEN_USER_SWITCHER, false)
- set(Flags.NSSL_DEBUG_LINES, false)
- }
- val sceneContainerFlags = FakeSceneContainerFlags().apply { enabled = true }
- val deviceEntryRepository: FakeDeviceEntryRepository by lazy { FakeDeviceEntryRepository() }
- val authenticationRepository: FakeAuthenticationRepository by lazy {
- FakeAuthenticationRepository(
- currentTime = { testScope.currentTime },
- )
- }
- val configurationRepository: FakeConfigurationRepository by lazy {
- FakeConfigurationRepository()
- }
- val configurationInteractor: ConfigurationInteractor by lazy {
- ConfigurationInteractor(configurationRepository)
- }
- private val emergencyServicesRepository: EmergencyServicesRepository by lazy {
- EmergencyServicesRepository(
- applicationScope = applicationScope(),
- resources = context.resources,
- configurationRepository = configurationRepository,
- )
- }
- val telephonyRepository: FakeTelephonyRepository by lazy { FakeTelephonyRepository() }
- val bouncerRepository = BouncerRepository(featureFlags)
- val communalRepository: FakeCommunalRepository by lazy { FakeCommunalRepository() }
- val keyguardRepository: FakeKeyguardRepository by lazy { FakeKeyguardRepository() }
- val powerRepository: FakePowerRepository by lazy { FakePowerRepository() }
- val simBouncerRepository: FakeSimBouncerRepository by lazy { FakeSimBouncerRepository() }
- val clock: SystemClock = mock {
- whenever(elapsedRealtime()).thenAnswer { testScope.currentTime }
- }
- val telephonyManager: TelephonyManager = mock {
- whenever(createForSubscriptionId(anyInt())).thenReturn(this)
- whenever(supplyIccLockPin(anyString())).thenReturn(PinResult(PIN_RESULT_TYPE_SUCCESS, 3))
- }
- val devicePolicyManager: DevicePolicyManager = mock {}
- val mobileConnectionsRepository: FakeMobileConnectionsRepository by lazy {
- FakeMobileConnectionsRepository(mock(), mock())
+ constructor(
+ context: Context,
+ ) {
+ kosmos.applicationContext = context
}
- val simBouncerInteractor =
- SimBouncerInteractor(
- applicationContext = context,
- backgroundDispatcher = testDispatcher,
- applicationScope = applicationScope(),
- repository = simBouncerRepository,
- telephonyManager = telephonyManager,
- resources = context.resources,
- keyguardUpdateMonitor = mock(),
- euiccManager = context.getSystemService(Context.EUICC_SERVICE) as EuiccManager,
- mobileConnectionsRepository = mobileConnectionsRepository,
- )
-
- val userRepository: FakeUserRepository by lazy {
- FakeUserRepository().apply {
- val users = listOf(UserInfo(/* id= */ 0, "name", /* flags= */ 0))
- setUserInfos(users)
- runBlocking { setSelectedUserInfo(users.first()) }
- }
+ constructor(testCase: SysuiTestCase) : this(context = testCase.context) {
+ kosmos.testCase = testCase
}
- private val falsingCollectorFake: FalsingCollector by lazy { FalsingCollectorFake() }
- private var falsingInteractor: FalsingInteractor? = null
- private var powerInteractor: PowerInteractor? = null
+ val testDispatcher by lazy { kosmos.testDispatcher }
+ val testScope by lazy { kosmos.testScope }
+ val fakeFeatureFlags by lazy { kosmos.fakeFeatureFlagsClassic }
+ val fakeSceneContainerFlags by lazy { kosmos.fakeSceneContainerFlags }
+ val deviceEntryRepository by lazy { kosmos.fakeDeviceEntryRepository }
+ val authenticationRepository by lazy { kosmos.fakeAuthenticationRepository }
+ val configurationRepository by lazy { kosmos.fakeConfigurationRepository }
+ val configurationInteractor by lazy { kosmos.configurationInteractor }
+ val telephonyRepository by lazy { kosmos.fakeTelephonyRepository }
+ val bouncerRepository by lazy { kosmos.bouncerRepository }
+ val communalRepository by lazy { kosmos.fakeCommunalRepository }
+ val keyguardRepository by lazy { kosmos.fakeKeyguardRepository }
+ val powerRepository by lazy { kosmos.fakePowerRepository }
+ val simBouncerRepository by lazy { kosmos.fakeSimBouncerRepository }
+ val clock by lazy { kosmos.systemClock }
+ val mobileConnectionsRepository by lazy { kosmos.fakeMobileConnectionsRepository }
+ val simBouncerInteractor by lazy { kosmos.simBouncerInteractor }
+ val statusBarStateController by lazy { kosmos.statusBarStateController }
+ val interactionJankMonitor by lazy { kosmos.interactionJankMonitor }
+ val screenOffAnimationController by lazy { kosmos.screenOffAnimationController }
- fun fakeSceneContainerRepository(
- containerConfig: SceneContainerConfig = fakeSceneContainerConfig(),
- ): SceneContainerRepository {
- return SceneContainerRepository(applicationScope(), containerConfig)
+ fun fakeSceneContainerRepository(): SceneContainerRepository {
+ return kosmos.sceneContainerRepository
}
fun fakeSceneKeys(): List<SceneKey> {
@@ -202,222 +128,59 @@
return kosmos.sceneContainerConfig
}
- @JvmOverloads
- fun sceneInteractor(
- repository: SceneContainerRepository = fakeSceneContainerRepository()
- ): SceneInteractor {
- return SceneInteractor(
- applicationScope = applicationScope(),
- repository = repository,
- powerInteractor = powerInteractor(),
- logger = mock(),
- )
+ fun sceneInteractor(): SceneInteractor {
+ return kosmos.sceneInteractor
}
- fun deviceEntryInteractor(
- repository: DeviceEntryRepository = deviceEntryRepository,
- authenticationInteractor: AuthenticationInteractor,
- sceneInteractor: SceneInteractor,
- faceAuthRepository: DeviceEntryFaceAuthRepository = FakeDeviceEntryFaceAuthRepository(),
- trustRepository: TrustRepository = FakeTrustRepository(),
- ): DeviceEntryInteractor {
- return DeviceEntryInteractor(
- applicationScope = applicationScope(),
- repository = repository,
- authenticationInteractor = authenticationInteractor,
- sceneInteractor = sceneInteractor,
- deviceEntryFaceAuthRepository = faceAuthRepository,
- trustRepository = trustRepository,
- flags = FakeSceneContainerFlags(enabled = true)
- )
+ fun deviceEntryInteractor(): DeviceEntryInteractor {
+ return kosmos.deviceEntryInteractor
}
- fun authenticationInteractor(
- repository: AuthenticationRepository = authenticationRepository,
- ): AuthenticationInteractor {
- return AuthenticationInteractor(
- applicationScope = applicationScope(),
- repository = repository,
- selectedUserInteractor = selectedUserInteractor(),
- )
+ fun authenticationInteractor(): AuthenticationInteractor {
+ return kosmos.authenticationInteractor
}
- fun keyguardInteractor(
- repository: KeyguardRepository = keyguardRepository
- ): KeyguardInteractor {
- return KeyguardInteractor(
- repository = repository,
- commandQueue = FakeCommandQueue(),
- sceneContainerFlags = sceneContainerFlags,
- bouncerRepository = FakeKeyguardBouncerRepository(),
- configurationInteractor = configurationInteractor,
- shadeRepository = FakeShadeRepository(),
- sceneInteractorProvider = { sceneInteractor() },
- powerInteractor = PowerInteractorFactory.create().powerInteractor,
- )
+ fun keyguardInteractor(): KeyguardInteractor {
+ return kosmos.keyguardInteractor
}
fun communalInteractor(): CommunalInteractor {
- return CommunalInteractorFactory.create(
- communalRepository = communalRepository,
- )
- .communalInteractor
+ return kosmos.communalInteractor
}
- fun bouncerInteractor(
- authenticationInteractor: AuthenticationInteractor,
- deviceEntryFaceAuthInteractor: DeviceEntryFaceAuthInteractor = mock(),
- ): BouncerInteractor {
- return BouncerInteractor(
- applicationScope = applicationScope(),
- applicationContext = context,
- repository = bouncerRepository,
- authenticationInteractor = authenticationInteractor,
- deviceEntryFaceAuthInteractor = deviceEntryFaceAuthInteractor,
- falsingInteractor = falsingInteractor(),
- powerInteractor = powerInteractor(),
- simBouncerInteractor = simBouncerInteractor,
- )
+ fun bouncerInteractor(): BouncerInteractor {
+ return kosmos.bouncerInteractor
}
- fun keyguardRootViewModel(): KeyguardRootViewModel = mock()
-
fun notificationsPlaceholderViewModel(): NotificationsPlaceholderViewModel {
- return NotificationsPlaceholderViewModel(
- interactor =
- NotificationStackAppearanceInteractor(
- repository = NotificationStackAppearanceRepository(),
- ),
- flags = sceneContainerFlags,
- featureFlags = featureFlags,
- )
+ return kosmos.notificationsPlaceholderViewModel
}
- fun bouncerViewModel(
- bouncerInteractor: BouncerInteractor,
- authenticationInteractor: AuthenticationInteractor,
- actionButtonInteractor: BouncerActionButtonInteractor = bouncerActionButtonInteractor(),
- users: List<UserViewModel> = createUsers(),
- ): BouncerViewModel {
- return BouncerViewModel(
- applicationContext = context,
- applicationScope = applicationScope(),
- mainDispatcher = testDispatcher,
- bouncerInteractor = bouncerInteractor,
- simBouncerInteractor = simBouncerInteractor,
- authenticationInteractor = authenticationInteractor,
- flags = sceneContainerFlags,
- selectedUser = flowOf(users.first { it.isSelectionMarkerVisible }),
- users = flowOf(users),
- userSwitcherMenu = flowOf(createMenuActions()),
- actionButton = actionButtonInteractor.actionButton,
- clock = clock,
- devicePolicyManager = devicePolicyManager,
- )
+ fun bouncerViewModel(): BouncerViewModel {
+ return kosmos.bouncerViewModel
}
- fun telephonyInteractor(
- repository: TelephonyRepository = telephonyRepository,
- ): TelephonyInteractor {
- return TelephonyInteractor(repository = repository)
+ fun telephonyInteractor(): TelephonyInteractor {
+ return kosmos.telephonyInteractor
}
- fun falsingInteractor(collector: FalsingCollector = falsingCollector()): FalsingInteractor {
- return falsingInteractor ?: FalsingInteractor(collector).also { falsingInteractor = it }
+ fun falsingInteractor(): FalsingInteractor {
+ return kosmos.falsingInteractor
}
fun falsingCollector(): FalsingCollector {
- return falsingCollectorFake
+ return kosmos.falsingCollector
}
- fun powerInteractor(
- repository: PowerRepository = powerRepository,
- falsingCollector: FalsingCollector = falsingCollector(),
- screenOffAnimationController: ScreenOffAnimationController = mock(),
- statusBarStateController: StatusBarStateController = mock(),
- ): PowerInteractor {
- return powerInteractor
- ?: PowerInteractor(
- repository = repository,
- falsingCollector = falsingCollector,
- screenOffAnimationController = screenOffAnimationController,
- statusBarStateController = statusBarStateController,
- )
- .also { powerInteractor = it }
- }
-
- private fun applicationScope(): CoroutineScope {
- return testScope.backgroundScope
- }
-
- private fun createUsers(
- count: Int = 3,
- selectedIndex: Int = 0,
- ): List<UserViewModel> {
- check(selectedIndex in 0 until count)
-
- return buildList {
- repeat(count) { index ->
- add(
- UserViewModel(
- viewKey = index,
- name = Text.Loaded("name_$index"),
- image = BitmapDrawable(Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888)),
- isSelectionMarkerVisible = index == selectedIndex,
- alpha = 1f,
- onClicked = {},
- )
- )
- }
- }
- }
-
- private fun createMenuActions(): List<UserActionViewModel> {
- return buildList {
- repeat(3) { index ->
- add(
- UserActionViewModel(
- viewKey = index.toLong(),
- iconResourceId = 0,
- textResourceId = 0,
- onClicked = {},
- )
- )
- }
- }
+ fun powerInteractor(): PowerInteractor {
+ return kosmos.powerInteractor
}
fun selectedUserInteractor(): SelectedUserInteractor {
- return SelectedUserInteractor(userRepository)
+ return kosmos.selectedUserInteractor
}
- fun bouncerActionButtonInteractor(
- mobileConnectionsRepository: MobileConnectionsRepository = mock(),
- activityTaskManager: ActivityTaskManager = mock(),
- telecomManager: TelecomManager? = null,
- emergencyAffordanceManager: EmergencyAffordanceManager =
- EmergencyAffordanceManager(context),
- emergencyDialerIntentFactory: EmergencyDialerIntentFactory =
- object : EmergencyDialerIntentFactory {
- override fun invoke(): Intent = Intent()
- },
- metricsLogger: MetricsLogger = mock(),
- dozeLogger: DozeLogger = mock(),
- ): BouncerActionButtonInteractor {
- return BouncerActionButtonInteractor(
- applicationContext = context,
- backgroundDispatcher = testDispatcher,
- repository = emergencyServicesRepository,
- mobileConnectionsRepository = mobileConnectionsRepository,
- telephonyInteractor = telephonyInteractor(),
- authenticationInteractor = authenticationInteractor(),
- selectedUserInteractor = selectedUserInteractor(),
- activityTaskManager = activityTaskManager,
- telecomManager = telecomManager,
- emergencyAffordanceManager = emergencyAffordanceManager,
- emergencyDialerIntentFactory = emergencyDialerIntentFactory,
- metricsLogger = metricsLogger,
- dozeLogger = dozeLogger,
- )
+ fun bouncerActionButtonInteractor(): BouncerActionButtonInteractor {
+ return kosmos.bouncerActionButtonInteractor
}
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryKosmos.kt
index 7c4e160..e19941c 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryKosmos.kt
@@ -18,7 +18,7 @@
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.applicationCoroutineScope
-import com.android.systemui.scene.shared.model.sceneContainerConfig
+import com.android.systemui.scene.sceneContainerConfig
val Kosmos.sceneContainerRepository by
Kosmos.Fixture { SceneContainerRepository(applicationCoroutineScope, sceneContainerConfig) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/shared/flag/SceneContainerFlagsKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/shared/flag/SceneContainerFlagsKosmos.kt
index c2cdbed..979d8e7 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/shared/flag/SceneContainerFlagsKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/shared/flag/SceneContainerFlagsKosmos.kt
@@ -18,4 +18,5 @@
import com.android.systemui.kosmos.Kosmos
-var Kosmos.sceneContainerFlags by Kosmos.Fixture { FakeSceneContainerFlags() }
+var Kosmos.fakeSceneContainerFlags by Kosmos.Fixture { FakeSceneContainerFlags() }
+val Kosmos.sceneContainerFlags by Kosmos.Fixture<SceneContainerFlags> { fakeSceneContainerFlags }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/shared/model/FakeSceneContainerConfigModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/shared/model/FakeSceneContainerConfigModule.kt
index b4fc948..8811b8d 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/shared/model/FakeSceneContainerConfigModule.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/shared/model/FakeSceneContainerConfigModule.kt
@@ -30,6 +30,7 @@
SceneKey.Lockscreen,
SceneKey.Bouncer,
SceneKey.Gone,
+ SceneKey.Communal,
),
initialSceneKey = SceneKey.Lockscreen,
),
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/smartspace/data/repository/SmartspaceRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/smartspace/data/repository/SmartspaceRepositoryKosmos.kt
index e671d45..0e4c923 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/smartspace/data/repository/SmartspaceRepositoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/smartspace/data/repository/SmartspaceRepositoryKosmos.kt
@@ -17,6 +17,8 @@
package com.android.systemui.smartspace.data.repository
import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.Kosmos.Fixture
-var Kosmos.smartspaceRepository: SmartspaceRepository by Kosmos.Fixture { fakeSmartspaceRepository }
-val Kosmos.fakeSmartspaceRepository by Kosmos.Fixture { FakeSmartspaceRepository() }
+val Kosmos.fakeSmartspaceRepository by Fixture { FakeSmartspaceRepository() }
+
+val Kosmos.smartspaceRepository by Fixture<SmartspaceRepository> { fakeSmartspaceRepository }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/LockscreenShadeScrimTransitionControllerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/LockscreenShadeScrimTransitionControllerKosmos.kt
index 93a7adf..8385403 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/LockscreenShadeScrimTransitionControllerKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/LockscreenShadeScrimTransitionControllerKosmos.kt
@@ -16,7 +16,7 @@
package com.android.systemui.statusbar
-import android.content.testableContext
+import android.content.applicationContext
import com.android.systemui.dump.dumpManager
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
@@ -27,7 +27,7 @@
val Kosmos.lockscreenShadeScrimTransitionController by Fixture {
LockscreenShadeScrimTransitionController(
scrimController = scrimController,
- context = testableContext,
+ context = applicationContext,
configurationController = configurationController,
dumpManager = dumpManager,
splitShadeStateController = splitShadeStateController,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerKosmos.kt
index 2752cc2..1c6ce79 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerKosmos.kt
@@ -16,7 +16,7 @@
package com.android.systemui.statusbar
-import android.content.testableContext
+import android.content.applicationContext
import com.android.systemui.classifier.falsingCollector
import com.android.systemui.classifier.falsingManager
import com.android.systemui.dump.dumpManager
@@ -47,7 +47,7 @@
scrimTransitionController = lockscreenShadeScrimTransitionController,
keyguardTransitionControllerFactory = lockscreenShadeKeyguardTransitionControllerFactory,
depthController = notificationShadeDepthController,
- context = testableContext,
+ context = applicationContext,
splitShadeOverScrollerFactory = splitShadeLockScreenOverScrollerFactory,
singleShadeOverScrollerFactory = singleShadeLockScreenOverScrollerFactory,
activityStarter = activityStarter,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotifCollectionKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/NotifCollectionKosmos.kt
similarity index 77%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotifCollectionKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/NotifCollectionKosmos.kt
index d98f496..9b27a9f 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotifCollectionKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/NotifCollectionKosmos.kt
@@ -14,11 +14,11 @@
* limitations under the License.
*/
-package com.android.systemui.statusbar.notification.stack.ui.viewbinder
+package com.android.systemui.statusbar.notification.collection
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
-import com.android.systemui.statusbar.notification.collection.NotifCollection
import com.android.systemui.util.mockito.mock
-var Kosmos.notifCollection by Fixture { mock<NotifCollection>() }
+val Kosmos.notifCollection by Fixture { mockNotifCollection }
+val Kosmos.mockNotifCollection by Fixture { mock<NotifCollection>() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/model/ActiveNotificationModelBuilder.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/model/ActiveNotificationModelBuilder.kt
index 9851b0e..9c5c486 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/model/ActiveNotificationModelBuilder.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/model/ActiveNotificationModelBuilder.kt
@@ -18,6 +18,7 @@
import android.graphics.drawable.Icon
import com.android.systemui.statusbar.notification.shared.ActiveNotificationModel
+import com.android.systemui.statusbar.notification.stack.BUCKET_UNKNOWN
/** Simple ActiveNotificationModel builder for use in tests. */
fun activeNotificationModel(
@@ -32,6 +33,11 @@
aodIcon: Icon? = null,
shelfIcon: Icon? = null,
statusBarIcon: Icon? = null,
+ uid: Int = 0,
+ instanceId: Int? = null,
+ isGroupSummary: Boolean = false,
+ packageName: String = "pkg",
+ bucket: Int = BUCKET_UNKNOWN,
) =
ActiveNotificationModel(
key = key,
@@ -45,4 +51,9 @@
aodIcon = aodIcon,
shelfIcon = shelfIcon,
statusBarIcon = statusBarIcon,
+ uid = uid,
+ packageName = packageName,
+ instanceId = instanceId,
+ isGroupSummary = isGroupSummary,
+ bucket = bucket,
)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/repository/ActiveNotificationListRepositoryExt.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/repository/ActiveNotificationListRepositoryExt.kt
index cb1ba20..b40e1e7 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/repository/ActiveNotificationListRepositoryExt.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/repository/ActiveNotificationListRepositoryExt.kt
@@ -20,11 +20,20 @@
/**
* Make the repository hold [count] active notifications for testing. The keys of the notifications
- * are "0", "1", ..., (count - 1).toString().
+ * are "0", "1", ..., (count - 1).toString(). The ranks are the same values in Int.
*/
fun ActiveNotificationListRepository.setActiveNotifs(count: Int) {
this.activeNotifications.value =
ActiveNotificationsStore.Builder()
- .apply { repeat(count) { i -> addEntry(activeNotificationModel(key = i.toString())) } }
+ .apply {
+ val rankingsMap = mutableMapOf<String, Int>()
+ repeat(count) { i ->
+ val key = "$i"
+ addEntry(activeNotificationModel(key = key))
+ rankingsMap[key] = i
+ }
+
+ setRankingsMap(rankingsMap)
+ }
.build()
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerViewBinderKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerViewBinderKosmos.kt
index 67fecb4..acc455f 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerViewBinderKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerViewBinderKosmos.kt
@@ -19,8 +19,8 @@
import com.android.systemui.common.ui.configurationState
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
+import com.android.systemui.statusbar.notification.collection.notifCollection
import com.android.systemui.statusbar.notification.icon.ui.viewmodel.notificationIconContainerShelfViewModel
-import com.android.systemui.statusbar.notification.stack.ui.viewbinder.notifCollection
import com.android.systemui.statusbar.ui.systemBarUtilsState
val Kosmos.notificationIconContainerShelfViewBinder by Fixture {
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotifCollectionKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/logging/NotificationPanelLogger.kt
similarity index 76%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotifCollectionKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/logging/NotificationPanelLogger.kt
index d98f496..30fc2f3 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotifCollectionKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/logging/NotificationPanelLogger.kt
@@ -14,11 +14,11 @@
* limitations under the License.
*/
-package com.android.systemui.statusbar.notification.stack.ui.viewbinder
+package com.android.systemui.statusbar.notification.logging
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
-import com.android.systemui.statusbar.notification.collection.NotifCollection
import com.android.systemui.util.mockito.mock
-var Kosmos.notifCollection by Fixture { mock<NotifCollection>() }
+val Kosmos.notificationPanelLogger by Fixture { mockNotificationPanelLogger }
+val Kosmos.mockNotificationPanelLogger by Fixture { mock<NotificationPanelLogger>() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/AmbientStateKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/AmbientStateKosmos.kt
index 83ac330..7f6f698 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/AmbientStateKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/AmbientStateKosmos.kt
@@ -16,7 +16,7 @@
package com.android.systemui.statusbar.notification.stack
-import android.content.testableContext
+import android.content.applicationContext
import com.android.systemui.dump.dumpManager
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
@@ -27,7 +27,7 @@
@OptIn(ExperimentalCoroutinesApi::class)
val Kosmos.ambientState by Fixture {
AmbientState(
- /*context=*/ testableContext,
+ /*context=*/ applicationContext,
/*dumpManager=*/ dumpManager,
/*sectionProvider=*/ stackScrollAlgorithmSectionProvider,
/*bypassController=*/ stackScrollAlgorithmBypassController,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/view/NotificationStatsLoggerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/view/NotificationStatsLoggerKosmos.kt
new file mode 100644
index 0000000..de52155
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/view/NotificationStatsLoggerKosmos.kt
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.stack.ui.view
+
+import android.service.notification.notificationListenerService
+import com.android.internal.statusbar.statusBarService
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.Kosmos.Fixture
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.statusbar.notification.logging.notificationPanelLogger
+
+val Kosmos.notificationStatsLogger by Fixture {
+ NotificationStatsLoggerImpl(
+ applicationScope = testScope,
+ bgDispatcher = testDispatcher,
+ statusBarService = statusBarService,
+ notificationListenerService = notificationListenerService,
+ notificationPanelLogger = notificationPanelLogger,
+ )
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinderKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinderKosmos.kt
index 04716b9..c6498e4 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinderKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinderKosmos.kt
@@ -23,8 +23,10 @@
import com.android.systemui.kosmos.Kosmos.Fixture
import com.android.systemui.kosmos.testDispatcher
import com.android.systemui.statusbar.notification.icon.ui.viewbinder.notificationIconContainerShelfViewBinder
+import com.android.systemui.statusbar.notification.stack.ui.view.notificationStatsLogger
import com.android.systemui.statusbar.notification.stack.ui.viewmodel.notificationListViewModel
import com.android.systemui.statusbar.phone.notificationIconAreaController
+import java.util.Optional
val Kosmos.notificationListViewBinder by Fixture {
NotificationListViewBinder(
@@ -35,5 +37,6 @@
iconAreaController = notificationIconAreaController,
metricsLogger = metricsLogger,
nicBinder = notificationIconContainerShelfViewBinder,
+ loggerOptional = Optional.of(notificationStatsLogger),
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotifCollectionKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListLoggerViewModelKosmos.kt
similarity index 61%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotifCollectionKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListLoggerViewModelKosmos.kt
index d98f496..08bda46 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotifCollectionKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListLoggerViewModelKosmos.kt
@@ -14,11 +14,17 @@
* limitations under the License.
*/
-package com.android.systemui.statusbar.notification.stack.ui.viewbinder
+package com.android.systemui.statusbar.notification.stack.ui.viewmodel
+import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
-import com.android.systemui.statusbar.notification.collection.NotifCollection
-import com.android.systemui.util.mockito.mock
+import com.android.systemui.statusbar.notification.domain.interactor.activeNotificationsInteractor
-var Kosmos.notifCollection by Fixture { mock<NotifCollection>() }
+val Kosmos.notificationListLoggerViewModel by Fixture {
+ NotificationLoggerViewModel(
+ keyguardInteractor = keyguardInteractor,
+ windowRootViewVisibilityInteractor = windowRootViewVisibilityInteractor,
+ activeNotificationsInteractor = activeNotificationsInteractor,
+ )
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelKosmos.kt
index f5a4c03..998e579 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelKosmos.kt
@@ -33,6 +33,7 @@
shelf = notificationShelfViewModel,
hideListViewModel = hideListViewModel,
footer = Optional.of(footerViewModel),
+ logger = Optional.of(notificationListLoggerViewModel),
activeNotificationsInteractor = activeNotificationsInteractor,
keyguardTransitionInteractor = keyguardTransitionInteractor,
seenNotificationsInteractor = seenNotificationsInteractor,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/WindowRootViewVisibilityInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/WindowRootViewVisibilityInteractorKosmos.kt
index e4313bb1..d9beabb 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/WindowRootViewVisibilityInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/WindowRootViewVisibilityInteractorKosmos.kt
@@ -19,7 +19,7 @@
import com.android.systemui.keyguard.data.repository.keyguardRepository
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
-import com.android.systemui.kosmos.testScope
+import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.power.domain.interactor.powerInteractor
import com.android.systemui.scene.data.repository.windowRootViewVisibilityRepository
import com.android.systemui.scene.domain.interactor.WindowRootViewVisibilityInteractor
@@ -27,7 +27,7 @@
val Kosmos.windowRootViewVisibilityInteractor by Fixture {
WindowRootViewVisibilityInteractor(
- scope = testScope,
+ scope = applicationCoroutineScope,
windowRootViewVisibilityRepository = windowRootViewVisibilityRepository,
keyguardRepository = keyguardRepository,
headsUpManager = headsUpManager,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotifCollectionKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionsRepositoryKosmos.kt
similarity index 65%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotifCollectionKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionsRepositoryKosmos.kt
index d98f496..9d62ea5 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotifCollectionKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionsRepositoryKosmos.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,11 +14,15 @@
* limitations under the License.
*/
-package com.android.systemui.statusbar.notification.stack.ui.viewbinder
+package com.android.systemui.statusbar.pipeline.mobile.data.repository
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
-import com.android.systemui.statusbar.notification.collection.NotifCollection
import com.android.systemui.util.mockito.mock
-var Kosmos.notifCollection by Fixture { mock<NotifCollection>() }
+val Kosmos.fakeMobileConnectionsRepository by Fixture {
+ FakeMobileConnectionsRepository(tableLogBuffer = mock())
+}
+
+val Kosmos.mobileConnectionsRepository by
+ Fixture<MobileConnectionsRepository> { fakeMobileConnectionsRepository }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotifCollectionKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelKosmos.kt
similarity index 60%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotifCollectionKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelKosmos.kt
index d98f496..0b9f897 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotifCollectionKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelKosmos.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,11 +14,16 @@
* limitations under the License.
*/
-package com.android.systemui.statusbar.notification.stack.ui.viewbinder
+package com.android.systemui.user.ui.viewmodel
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
-import com.android.systemui.statusbar.notification.collection.NotifCollection
-import com.android.systemui.util.mockito.mock
+import com.android.systemui.user.domain.interactor.guestUserInteractor
+import com.android.systemui.user.domain.interactor.userSwitcherInteractor
-var Kosmos.notifCollection by Fixture { mock<NotifCollection>() }
+val Kosmos.userSwitcherViewModel by Fixture {
+ UserSwitcherViewModel(
+ userSwitcherInteractor = userSwitcherInteractor,
+ guestUserInteractor = guestUserInteractor,
+ )
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/util/concurrency/FakeExecutorModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/util/concurrency/FakeExecutorModule.kt
index 1f48d94..11c09ee 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/util/concurrency/FakeExecutorModule.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/util/concurrency/FakeExecutorModule.kt
@@ -17,6 +17,7 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.dagger.qualifiers.UiBackground
import com.android.systemui.util.time.FakeSystemClock
import dagger.Binds
import dagger.Module
@@ -27,8 +28,9 @@
interface FakeExecutorModule {
@Binds @Main @SysUISingleton fun bindMainExecutor(executor: FakeExecutor): Executor
+ @Binds @UiBackground @SysUISingleton fun bindUiBgExecutor(executor: FakeExecutor): Executor
+
companion object {
- @Provides
- fun provideFake(clock: FakeSystemClock) = FakeExecutor(clock)
+ @Provides fun provideFake(clock: FakeSystemClock) = FakeExecutor(clock)
}
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/util/time/FakeSystemClockKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/util/time/FakeSystemClockKosmos.kt
index 914e654..f3a8b14 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/util/time/FakeSystemClockKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/util/time/FakeSystemClockKosmos.kt
@@ -17,5 +17,19 @@
package com.android.systemui.util.time
import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.whenever
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.currentTime
-var Kosmos.fakeSystemClock by Kosmos.Fixture { FakeSystemClock() }
+@OptIn(ExperimentalCoroutinesApi::class)
+val Kosmos.systemClock by
+ Kosmos.Fixture<SystemClock> {
+ mock {
+ whenever(elapsedRealtime()).thenAnswer { testScope.currentTime }
+ whenever(uptimeMillis()).thenAnswer { testScope.currentTime }
+ }
+ }
+
+val Kosmos.fakeSystemClock by Kosmos.Fixture { FakeSystemClock() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotifCollectionKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/telecom/TelecomManagerKosmos.kt
similarity index 71%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotifCollectionKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/telecom/TelecomManagerKosmos.kt
index d98f496..4e0c0883 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotifCollectionKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/telecom/TelecomManagerKosmos.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,11 +14,11 @@
* limitations under the License.
*/
-package com.android.systemui.statusbar.notification.stack.ui.viewbinder
+package com.android.telecom
+import android.telecom.TelecomManager
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
-import com.android.systemui.statusbar.notification.collection.NotifCollection
import com.android.systemui.util.mockito.mock
-var Kosmos.notifCollection by Fixture { mock<NotifCollection>() }
+var Kosmos.telecomManager by Fixture<TelecomManager?> { mock() }
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 6a81425..a856f42 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -4271,13 +4271,19 @@
if (value != null) {
viewState.setCurrentValue(value);
}
-
+ boolean isCredmanRequested = (flags & FLAG_VIEW_REQUESTS_CREDMAN_SERVICE) != 0;
if (shouldRequestSecondaryProvider(flags)) {
if (requestNewFillResponseOnViewEnteredIfNecessaryLocked(
id, viewState, flags)) {
Slog.v(TAG, "Started a new fill request for secondary provider.");
return;
}
+
+ FillResponse response = viewState.getSecondaryResponse();
+ if (response != null) {
+ logPresentationStatsOnViewEntered(response, isCredmanRequested);
+ }
+
// If the ViewState is ready to be displayed, onReady() will be called.
viewState.update(value, virtualBounds, flags);
@@ -4363,15 +4369,9 @@
return;
}
- if (viewState.getResponse() != null) {
- boolean isCredmanRequested = (flags & FLAG_VIEW_REQUESTS_CREDMAN_SERVICE) != 0;
- FillResponse response = viewState.getResponse();
- mPresentationStatsEventLogger.maybeSetRequestId(response.getRequestId());
- mPresentationStatsEventLogger.maybeSetIsCredentialRequest(isCredmanRequested);
- mPresentationStatsEventLogger.maybeSetFieldClassificationRequestId(
- mFieldClassificationIdSnapshot);
- mPresentationStatsEventLogger.maybeSetAvailableCount(
- response.getDatasets(), mCurrentViewId);
+ FillResponse response = viewState.getResponse();
+ if (response != null) {
+ logPresentationStatsOnViewEntered(response, isCredmanRequested);
}
if (isSameViewEntered) {
@@ -4412,6 +4412,17 @@
}
@GuardedBy("mLock")
+ private void logPresentationStatsOnViewEntered(FillResponse response,
+ boolean isCredmanRequested) {
+ mPresentationStatsEventLogger.maybeSetRequestId(response.getRequestId());
+ mPresentationStatsEventLogger.maybeSetIsCredentialRequest(isCredmanRequested);
+ mPresentationStatsEventLogger.maybeSetFieldClassificationRequestId(
+ mFieldClassificationIdSnapshot);
+ mPresentationStatsEventLogger.maybeSetAvailableCount(
+ response.getDatasets(), mCurrentViewId);
+ }
+
+ @GuardedBy("mLock")
private void hideAugmentedAutofillLocked(@NonNull ViewState viewState) {
if ((viewState.getState()
& ViewState.STATE_TRIGGERED_AUGMENTED_AUTOFILL) != 0) {
diff --git a/services/core/java/com/android/server/biometrics/log/BiometricContext.java b/services/core/java/com/android/server/biometrics/log/BiometricContext.java
index 3dcea19..f8a9867 100644
--- a/services/core/java/com/android/server/biometrics/log/BiometricContext.java
+++ b/services/core/java/com/android/server/biometrics/log/BiometricContext.java
@@ -77,6 +77,9 @@
@AuthenticateOptions.DisplayState
int getDisplayState();
+ /** Gets whether touches on sensor are ignored by HAL */
+ boolean isHardwareIgnoringTouches();
+
/**
* Subscribe to context changes.
*
diff --git a/services/core/java/com/android/server/biometrics/log/BiometricContextProvider.java b/services/core/java/com/android/server/biometrics/log/BiometricContextProvider.java
index 95a047f..535b7b7 100644
--- a/services/core/java/com/android/server/biometrics/log/BiometricContextProvider.java
+++ b/services/core/java/com/android/server/biometrics/log/BiometricContextProvider.java
@@ -86,8 +86,8 @@
@Nullable private final Handler mHandler;
private int mDockState = Intent.EXTRA_DOCK_STATE_UNDOCKED;
private int mFoldState = IBiometricContextListener.FoldState.UNKNOWN;
-
private int mDisplayState = AuthenticateOptions.DISPLAY_STATE_UNKNOWN;
+ private boolean mIsHardwareIgnoringTouches = false;
@VisibleForTesting
final BroadcastReceiver mDockStateReceiver = new BroadcastReceiver() {
@Override
@@ -129,6 +129,14 @@
notifyChanged();
}
}
+
+ @Override
+ public void onHardwareIgnoreTouchesChanged(boolean shouldIgnore) {
+ if (mIsHardwareIgnoringTouches != shouldIgnore) {
+ mIsHardwareIgnoringTouches = shouldIgnore;
+ notifyChanged();
+ }
+ }
});
service.registerSessionListener(SESSION_TYPES, new ISessionListener.Stub() {
@Override
@@ -215,6 +223,11 @@
}
@Override
+ public boolean isHardwareIgnoringTouches() {
+ return mIsHardwareIgnoringTouches;
+ }
+
+ @Override
public void subscribe(@NonNull OperationContextExt context,
@NonNull Consumer<OperationContext> consumer) {
mSubscribers.put(context, consumer);
@@ -254,6 +267,7 @@
+ "bp session: " + getBiometricPromptSessionInfo() + ", "
+ "displayState: " + getDisplayState() + ", "
+ "isAwake: " + isAwake() + ", "
+ + "isHardwareIgnoring: " + isHardwareIgnoringTouches() + ", "
+ "isDisplayOn: " + isDisplayOn() + ", "
+ "dock: " + getDockedState() + ", "
+ "rotation: " + getCurrentRotation() + ", "
diff --git a/services/core/java/com/android/server/biometrics/log/OperationContextExt.java b/services/core/java/com/android/server/biometrics/log/OperationContextExt.java
index b4e0dff..0045d44 100644
--- a/services/core/java/com/android/server/biometrics/log/OperationContextExt.java
+++ b/services/core/java/com/android/server/biometrics/log/OperationContextExt.java
@@ -20,12 +20,14 @@
import android.annotation.Nullable;
import android.content.Intent;
import android.hardware.biometrics.AuthenticateOptions;
+import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.IBiometricContextListener;
import android.hardware.biometrics.common.AuthenticateReason;
import android.hardware.biometrics.common.DisplayState;
import android.hardware.biometrics.common.FoldState;
import android.hardware.biometrics.common.OperationContext;
import android.hardware.biometrics.common.OperationReason;
+import android.hardware.biometrics.common.OperationState;
import android.hardware.biometrics.common.WakeReason;
import android.hardware.face.FaceAuthenticateOptions;
import android.hardware.fingerprint.FingerprintAuthenticateOptions;
@@ -51,13 +53,26 @@
/** Create a context. */
public OperationContextExt(boolean isBP) {
- this(new OperationContext(), isBP);
+ this(new OperationContext(), isBP, BiometricAuthenticator.TYPE_NONE);
+ }
+
+ public OperationContextExt(boolean isBP, @BiometricAuthenticator.Modality int modality) {
+ this(new OperationContext(), isBP, modality);
}
/** Create a wrapped context. */
- public OperationContextExt(@NonNull OperationContext context, boolean isBP) {
+ public OperationContextExt(@NonNull OperationContext context, boolean isBP,
+ @BiometricAuthenticator.Modality int modality) {
mAidlContext = context;
mIsBP = isBP;
+
+ if (modality == BiometricAuthenticator.TYPE_FINGERPRINT) {
+ mAidlContext.operationState = OperationState.fingerprintOperationState(
+ new OperationState.FingerprintOperationState());
+ } else if (modality == BiometricAuthenticator.TYPE_FACE) {
+ mAidlContext.operationState = OperationState.faceOperationState(
+ new OperationState.FaceOperationState());
+ }
}
/**
@@ -247,12 +262,23 @@
return mOrientation;
}
+ /** The current operation state */
+ public OperationState getOperationState() {
+ return mAidlContext.operationState;
+ }
+
/** Update this object with the latest values from the given context. */
OperationContextExt update(@NonNull BiometricContext biometricContext, boolean isCrypto) {
mAidlContext.isAod = biometricContext.isAod();
mAidlContext.displayState = toAidlDisplayState(biometricContext.getDisplayState());
mAidlContext.foldState = toAidlFoldState(biometricContext.getFoldState());
mAidlContext.isCrypto = isCrypto;
+
+ if (mAidlContext.operationState != null && mAidlContext.operationState.getTag()
+ == OperationState.fingerprintOperationState) {
+ mAidlContext.operationState.getFingerprintOperationState().isHardwareIgnoringTouches =
+ biometricContext.isHardwareIgnoringTouches();
+ }
setFirstSessionId(biometricContext);
mIsDisplayOn = biometricContext.isDisplayOn();
diff --git a/services/core/java/com/android/server/biometrics/sensors/ClientMonitorCallbackConverter.java b/services/core/java/com/android/server/biometrics/sensors/ClientMonitorCallbackConverter.java
index 1a682a9..1fc7c70 100644
--- a/services/core/java/com/android/server/biometrics/sensors/ClientMonitorCallbackConverter.java
+++ b/services/core/java/com/android/server/biometrics/sensors/ClientMonitorCallbackConverter.java
@@ -41,22 +41,42 @@
* a common interface.
*/
public class ClientMonitorCallbackConverter {
- private IBiometricSensorReceiver mSensorReceiver; // BiometricService
- private IFaceServiceReceiver mFaceServiceReceiver; // FaceManager
- private IFingerprintServiceReceiver mFingerprintServiceReceiver; // FingerprintManager
+ private final IBiometricSensorReceiver mSensorReceiver; // BiometricService
+ private final IFaceServiceReceiver mFaceServiceReceiver; // FaceManager
+ private final IFingerprintServiceReceiver mFingerprintServiceReceiver; // FingerprintManager
public ClientMonitorCallbackConverter(IBiometricSensorReceiver sensorReceiver) {
mSensorReceiver = sensorReceiver;
+ mFaceServiceReceiver = null;
+ mFingerprintServiceReceiver = null;
}
public ClientMonitorCallbackConverter(IFaceServiceReceiver faceServiceReceiver) {
+ mSensorReceiver = null;
mFaceServiceReceiver = faceServiceReceiver;
+ mFingerprintServiceReceiver = null;
}
public ClientMonitorCallbackConverter(IFingerprintServiceReceiver fingerprintServiceReceiver) {
+ mSensorReceiver = null;
+ mFaceServiceReceiver = null;
mFingerprintServiceReceiver = fingerprintServiceReceiver;
}
+ /**
+ * Returns an int representing the {@link BiometricAuthenticator.Modality} of the active
+ * ServiceReceiver
+ */
+ @BiometricAuthenticator.Modality
+ public int getModality() {
+ if (mFaceServiceReceiver != null) {
+ return BiometricAuthenticator.TYPE_FACE;
+ } else if (mFingerprintServiceReceiver != null) {
+ return BiometricAuthenticator.TYPE_FINGERPRINT;
+ }
+ return BiometricAuthenticator.TYPE_NONE;
+ }
+
// The following apply to all clients
public void onAcquired(int sensorId, int acquiredInfo, int vendorCode) throws RemoteException {
diff --git a/services/core/java/com/android/server/biometrics/sensors/HalClientMonitor.java b/services/core/java/com/android/server/biometrics/sensors/HalClientMonitor.java
index 03658ce..0f01510 100644
--- a/services/core/java/com/android/server/biometrics/sensors/HalClientMonitor.java
+++ b/services/core/java/com/android/server/biometrics/sensors/HalClientMonitor.java
@@ -19,6 +19,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
+import android.hardware.biometrics.BiometricAuthenticator;
import android.os.IBinder;
import com.android.server.biometrics.log.BiometricContext;
@@ -58,7 +59,8 @@
super(context, token, listener, userId, owner, cookie, sensorId,
biometricLogger, biometricContext);
mLazyDaemon = lazyDaemon;
- mOperationContext = new OperationContextExt(isBiometricPrompt());
+ int modality = listener != null ? listener.getModality() : BiometricAuthenticator.TYPE_NONE;
+ mOperationContext = new OperationContextExt(isBiometricPrompt(), modality);
}
@Nullable
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
index 29c5a3d..145885d 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
@@ -28,6 +28,7 @@
import android.hardware.biometrics.BiometricFingerprintConstants.FingerprintAcquired;
import android.hardware.biometrics.BiometricManager.Authenticators;
import android.hardware.biometrics.common.ICancellationSignal;
+import android.hardware.biometrics.common.OperationState;
import android.hardware.biometrics.fingerprint.PointerContext;
import android.hardware.fingerprint.FingerprintAuthenticateOptions;
import android.hardware.fingerprint.FingerprintManager;
@@ -326,6 +327,12 @@
if (session.hasContextMethods()) {
try {
session.getSession().onContextChanged(ctx);
+ // TODO(b/317414324): Deprecate setIgnoreDisplayTouches
+ if (ctx.operationState != null && ctx.operationState.getTag()
+ == OperationState.fingerprintOperationState) {
+ session.getSession().setIgnoreDisplayTouches(
+ ctx.operationState.getFingerprintOperationState().isHardwareIgnoringTouches);
+ }
} catch (RemoteException e) {
Slog.e(TAG, "Unable to notify context changed", e);
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java
index e58e5ae..3aab7b3 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java
@@ -23,6 +23,7 @@
import android.content.Context;
import android.hardware.biometrics.BiometricRequestConstants;
import android.hardware.biometrics.common.ICancellationSignal;
+import android.hardware.biometrics.common.OperationState;
import android.hardware.fingerprint.FingerprintAuthenticateOptions;
import android.hardware.fingerprint.IUdfpsOverlayController;
import android.os.IBinder;
@@ -122,6 +123,12 @@
getBiometricContext().subscribe(opContext, ctx -> {
try {
session.getSession().onContextChanged(ctx);
+ // TODO(b/317414324): Deprecate setIgnoreDisplayTouches
+ if (ctx.operationState != null && ctx.operationState.getTag()
+ == OperationState.fingerprintOperationState) {
+ session.getSession().setIgnoreDisplayTouches(
+ ctx.operationState.getFingerprintOperationState().isHardwareIgnoringTouches);
+ }
} catch (RemoteException e) {
Slog.e(TAG, "Unable to notify context changed", e);
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java
index c0761ed..bf5011d 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java
@@ -26,6 +26,7 @@
import android.hardware.biometrics.BiometricFingerprintConstants.FingerprintAcquired;
import android.hardware.biometrics.BiometricStateListener;
import android.hardware.biometrics.common.ICancellationSignal;
+import android.hardware.biometrics.common.OperationState;
import android.hardware.biometrics.fingerprint.PointerContext;
import android.hardware.fingerprint.Fingerprint;
import android.hardware.fingerprint.FingerprintManager;
@@ -220,6 +221,12 @@
getBiometricContext().subscribe(opContext, ctx -> {
try {
session.getSession().onContextChanged(ctx);
+ // TODO(b/317414324): Deprecate setIgnoreDisplayTouches
+ if (ctx.operationState != null && ctx.operationState.getTag()
+ == OperationState.fingerprintOperationState) {
+ session.getSession().setIgnoreDisplayTouches(
+ ctx.operationState.getFingerprintOperationState().isHardwareIgnoringTouches);
+ }
} catch (RemoteException e) {
Slog.e(TAG, "Unable to notify context changed", e);
}
diff --git a/services/core/java/com/android/server/inputmethod/HardwareKeyboardShortcutController.java b/services/core/java/com/android/server/inputmethod/HardwareKeyboardShortcutController.java
index 898d5a5..ad27c52 100644
--- a/services/core/java/com/android/server/inputmethod/HardwareKeyboardShortcutController.java
+++ b/services/core/java/com/android/server/inputmethod/HardwareKeyboardShortcutController.java
@@ -53,8 +53,7 @@
@GuardedBy("ImfLock.class")
void reset(@NonNull ArrayMap<String, InputMethodInfo> methodMap) {
mSubtypeHandles.clear();
- final InputMethodUtils.InputMethodSettings settings =
- new InputMethodUtils.InputMethodSettings(methodMap, mUserId);
+ final InputMethodSettings settings = new InputMethodSettings(methodMap, mUserId);
for (final InputMethodInfo imi : settings.getEnabledInputMethodListLocked()) {
if (!imi.shouldShowInInputMethodPicker()) {
continue;
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 4db9ead..622a2de 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -183,7 +183,6 @@
import com.android.server.input.InputManagerInternal;
import com.android.server.inputmethod.InputMethodManagerInternal.InputMethodListListener;
import com.android.server.inputmethod.InputMethodSubtypeSwitchingController.ImeSubtypeListItem;
-import com.android.server.inputmethod.InputMethodUtils.InputMethodSettings;
import com.android.server.pm.UserManagerInternal;
import com.android.server.statusbar.StatusBarManagerInternal;
import com.android.server.utils.PriorityDump;
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodSettings.java b/services/core/java/com/android/server/inputmethod/InputMethodSettings.java
new file mode 100644
index 0000000..4c7d755
--- /dev/null
+++ b/services/core/java/com/android/server/inputmethod/InputMethodSettings.java
@@ -0,0 +1,686 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.inputmethod;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.content.pm.PackageManagerInternal;
+import android.os.LocaleList;
+import android.provider.Settings;
+import android.text.TextUtils;
+import android.util.ArrayMap;
+import android.util.IntArray;
+import android.util.Pair;
+import android.util.Printer;
+import android.util.Slog;
+import android.view.inputmethod.InputMethodInfo;
+import android.view.inputmethod.InputMethodSubtype;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.function.Predicate;
+
+/**
+ * Utility class for putting and getting settings for InputMethod.
+ *
+ * <p>This is used in two ways:</p>
+ * <ul>
+ * <li>Singleton instance in {@link InputMethodManagerService}, which is updated on
+ * user-switch to follow the current user.</li>
+ * <li>On-demand instances when we need settings for non-current users.</li>
+ * </ul>
+ */
+final class InputMethodSettings {
+ public static final boolean DEBUG = false;
+ private static final String TAG = "InputMethodSettings";
+
+ private static final int NOT_A_SUBTYPE_ID = InputMethodUtils.NOT_A_SUBTYPE_ID;
+ private static final String NOT_A_SUBTYPE_ID_STR = String.valueOf(NOT_A_SUBTYPE_ID);
+ private static final char INPUT_METHOD_SEPARATOR = InputMethodUtils.INPUT_METHOD_SEPARATOR;
+ private static final char INPUT_METHOD_SUBTYPE_SEPARATOR =
+ InputMethodUtils.INPUT_METHOD_SUBTYPE_SEPARATOR;
+
+ private final ArrayMap<String, InputMethodInfo> mMethodMap;
+ @UserIdInt
+ private final int mCurrentUserId;
+
+ private static void buildEnabledInputMethodsSettingString(
+ StringBuilder builder, Pair<String, ArrayList<String>> ime) {
+ builder.append(ime.first);
+ // Inputmethod and subtypes are saved in the settings as follows:
+ // ime0;subtype0;subtype1:ime1;subtype0:ime2:ime3;subtype0;subtype1
+ for (String subtypeId : ime.second) {
+ builder.append(INPUT_METHOD_SUBTYPE_SEPARATOR).append(subtypeId);
+ }
+ }
+
+ InputMethodSettings(ArrayMap<String, InputMethodInfo> methodMap, @UserIdInt int userId) {
+ mMethodMap = methodMap;
+ mCurrentUserId = userId;
+ String ime = getSelectedInputMethod();
+ String defaultDeviceIme = getSelectedDefaultDeviceInputMethod();
+ if (defaultDeviceIme != null && !Objects.equals(ime, defaultDeviceIme)) {
+ putSelectedInputMethod(defaultDeviceIme);
+ putSelectedDefaultDeviceInputMethod(null);
+ }
+ }
+
+ private void putString(@NonNull String key, @Nullable String str) {
+ SecureSettingsWrapper.putString(key, str, mCurrentUserId);
+ }
+
+ @Nullable
+ private String getString(@NonNull String key, @Nullable String defaultValue) {
+ return SecureSettingsWrapper.getString(key, defaultValue, mCurrentUserId);
+ }
+
+ private void putInt(String key, int value) {
+ SecureSettingsWrapper.putInt(key, value, mCurrentUserId);
+ }
+
+ private int getInt(String key, int defaultValue) {
+ return SecureSettingsWrapper.getInt(key, defaultValue, mCurrentUserId);
+ }
+
+ ArrayList<InputMethodInfo> getEnabledInputMethodListLocked() {
+ return getEnabledInputMethodListWithFilterLocked(null /* matchingCondition */);
+ }
+
+ @NonNull
+ ArrayList<InputMethodInfo> getEnabledInputMethodListWithFilterLocked(
+ @Nullable Predicate<InputMethodInfo> matchingCondition) {
+ return createEnabledInputMethodListLocked(
+ getEnabledInputMethodsAndSubtypeListLocked(), matchingCondition);
+ }
+
+ List<InputMethodSubtype> getEnabledInputMethodSubtypeListLocked(
+ InputMethodInfo imi, boolean allowsImplicitlyEnabledSubtypes) {
+ List<InputMethodSubtype> enabledSubtypes =
+ getEnabledInputMethodSubtypeListLocked(imi);
+ if (allowsImplicitlyEnabledSubtypes && enabledSubtypes.isEmpty()) {
+ enabledSubtypes = SubtypeUtils.getImplicitlyApplicableSubtypesLocked(
+ SystemLocaleWrapper.get(mCurrentUserId), imi);
+ }
+ return InputMethodSubtype.sort(imi, enabledSubtypes);
+ }
+
+ List<InputMethodSubtype> getEnabledInputMethodSubtypeListLocked(InputMethodInfo imi) {
+ List<Pair<String, ArrayList<String>>> imsList =
+ getEnabledInputMethodsAndSubtypeListLocked();
+ ArrayList<InputMethodSubtype> enabledSubtypes = new ArrayList<>();
+ if (imi != null) {
+ for (Pair<String, ArrayList<String>> imsPair : imsList) {
+ InputMethodInfo info = mMethodMap.get(imsPair.first);
+ if (info != null && info.getId().equals(imi.getId())) {
+ final int subtypeCount = info.getSubtypeCount();
+ for (int i = 0; i < subtypeCount; ++i) {
+ InputMethodSubtype ims = info.getSubtypeAt(i);
+ for (String s : imsPair.second) {
+ if (String.valueOf(ims.hashCode()).equals(s)) {
+ enabledSubtypes.add(ims);
+ }
+ }
+ }
+ break;
+ }
+ }
+ }
+ return enabledSubtypes;
+ }
+
+ List<Pair<String, ArrayList<String>>> getEnabledInputMethodsAndSubtypeListLocked() {
+ final String enabledInputMethodsStr = getEnabledInputMethodsStr();
+ final TextUtils.SimpleStringSplitter inputMethodSplitter =
+ new TextUtils.SimpleStringSplitter(INPUT_METHOD_SEPARATOR);
+ final TextUtils.SimpleStringSplitter subtypeSplitter =
+ new TextUtils.SimpleStringSplitter(INPUT_METHOD_SUBTYPE_SEPARATOR);
+ final ArrayList<Pair<String, ArrayList<String>>> imsList = new ArrayList<>();
+ if (TextUtils.isEmpty(enabledInputMethodsStr)) {
+ return imsList;
+ }
+ inputMethodSplitter.setString(enabledInputMethodsStr);
+ while (inputMethodSplitter.hasNext()) {
+ String nextImsStr = inputMethodSplitter.next();
+ subtypeSplitter.setString(nextImsStr);
+ if (subtypeSplitter.hasNext()) {
+ ArrayList<String> subtypeHashes = new ArrayList<>();
+ // The first element is ime id.
+ String imeId = subtypeSplitter.next();
+ while (subtypeSplitter.hasNext()) {
+ subtypeHashes.add(subtypeSplitter.next());
+ }
+ imsList.add(new Pair<>(imeId, subtypeHashes));
+ }
+ }
+ return imsList;
+ }
+
+ /**
+ * Build and put a string of EnabledInputMethods with removing specified Id.
+ *
+ * @return the specified id was removed or not.
+ */
+ boolean buildAndPutEnabledInputMethodsStrRemovingIdLocked(
+ StringBuilder builder, List<Pair<String, ArrayList<String>>> imsList, String id) {
+ boolean isRemoved = false;
+ boolean needsAppendSeparator = false;
+ for (Pair<String, ArrayList<String>> ims : imsList) {
+ String curId = ims.first;
+ if (curId.equals(id)) {
+ // We are disabling this input method, and it is
+ // currently enabled. Skip it to remove from the
+ // new list.
+ isRemoved = true;
+ } else {
+ if (needsAppendSeparator) {
+ builder.append(INPUT_METHOD_SEPARATOR);
+ } else {
+ needsAppendSeparator = true;
+ }
+ buildEnabledInputMethodsSettingString(builder, ims);
+ }
+ }
+ if (isRemoved) {
+ // Update the setting with the new list of input methods.
+ putEnabledInputMethodsStr(builder.toString());
+ }
+ return isRemoved;
+ }
+
+ private ArrayList<InputMethodInfo> createEnabledInputMethodListLocked(
+ List<Pair<String, ArrayList<String>>> imsList,
+ Predicate<InputMethodInfo> matchingCondition) {
+ final ArrayList<InputMethodInfo> res = new ArrayList<>();
+ for (Pair<String, ArrayList<String>> ims : imsList) {
+ InputMethodInfo info = mMethodMap.get(ims.first);
+ if (info != null && !info.isVrOnly()
+ && (matchingCondition == null || matchingCondition.test(info))) {
+ res.add(info);
+ }
+ }
+ return res;
+ }
+
+ void putEnabledInputMethodsStr(@Nullable String str) {
+ if (DEBUG) {
+ Slog.d(TAG, "putEnabledInputMethodStr: " + str);
+ }
+ if (TextUtils.isEmpty(str)) {
+ // OK to coalesce to null, since getEnabledInputMethodsStr() can take care of the
+ // empty data scenario.
+ putString(Settings.Secure.ENABLED_INPUT_METHODS, null);
+ } else {
+ putString(Settings.Secure.ENABLED_INPUT_METHODS, str);
+ }
+ }
+
+ @NonNull
+ String getEnabledInputMethodsStr() {
+ return getString(Settings.Secure.ENABLED_INPUT_METHODS, "");
+ }
+
+ private void saveSubtypeHistory(
+ List<Pair<String, String>> savedImes, String newImeId, String newSubtypeId) {
+ StringBuilder builder = new StringBuilder();
+ boolean isImeAdded = false;
+ if (!TextUtils.isEmpty(newImeId) && !TextUtils.isEmpty(newSubtypeId)) {
+ builder.append(newImeId).append(INPUT_METHOD_SUBTYPE_SEPARATOR).append(
+ newSubtypeId);
+ isImeAdded = true;
+ }
+ for (Pair<String, String> ime : savedImes) {
+ String imeId = ime.first;
+ String subtypeId = ime.second;
+ if (TextUtils.isEmpty(subtypeId)) {
+ subtypeId = NOT_A_SUBTYPE_ID_STR;
+ }
+ if (isImeAdded) {
+ builder.append(INPUT_METHOD_SEPARATOR);
+ } else {
+ isImeAdded = true;
+ }
+ builder.append(imeId).append(INPUT_METHOD_SUBTYPE_SEPARATOR).append(
+ subtypeId);
+ }
+ // Remove the last INPUT_METHOD_SEPARATOR
+ putSubtypeHistoryStr(builder.toString());
+ }
+
+ private void addSubtypeToHistory(String imeId, String subtypeId) {
+ List<Pair<String, String>> subtypeHistory = loadInputMethodAndSubtypeHistoryLocked();
+ for (Pair<String, String> ime : subtypeHistory) {
+ if (ime.first.equals(imeId)) {
+ if (DEBUG) {
+ Slog.v(TAG, "Subtype found in the history: " + imeId + ", "
+ + ime.second);
+ }
+ // We should break here
+ subtypeHistory.remove(ime);
+ break;
+ }
+ }
+ if (DEBUG) {
+ Slog.v(TAG, "Add subtype to the history: " + imeId + ", " + subtypeId);
+ }
+ saveSubtypeHistory(subtypeHistory, imeId, subtypeId);
+ }
+
+ private void putSubtypeHistoryStr(@NonNull String str) {
+ if (DEBUG) {
+ Slog.d(TAG, "putSubtypeHistoryStr: " + str);
+ }
+ if (TextUtils.isEmpty(str)) {
+ // OK to coalesce to null, since getSubtypeHistoryStr() can take care of the empty
+ // data scenario.
+ putString(Settings.Secure.INPUT_METHODS_SUBTYPE_HISTORY, null);
+ } else {
+ putString(Settings.Secure.INPUT_METHODS_SUBTYPE_HISTORY, str);
+ }
+ }
+
+ Pair<String, String> getLastInputMethodAndSubtypeLocked() {
+ // Gets the first one from the history
+ return getLastSubtypeForInputMethodLockedInternal(null);
+ }
+
+ @Nullable
+ InputMethodSubtype getLastInputMethodSubtypeLocked() {
+ final Pair<String, String> lastIme = getLastInputMethodAndSubtypeLocked();
+ // TODO: Handle the case of the last IME with no subtypes
+ if (lastIme == null || TextUtils.isEmpty(lastIme.first)
+ || TextUtils.isEmpty(lastIme.second)) {
+ return null;
+ }
+ final InputMethodInfo lastImi = mMethodMap.get(lastIme.first);
+ if (lastImi == null) return null;
+ try {
+ final int lastSubtypeHash = Integer.parseInt(lastIme.second);
+ final int lastSubtypeId = SubtypeUtils.getSubtypeIdFromHashCode(lastImi,
+ lastSubtypeHash);
+ if (lastSubtypeId < 0 || lastSubtypeId >= lastImi.getSubtypeCount()) {
+ return null;
+ }
+ return lastImi.getSubtypeAt(lastSubtypeId);
+ } catch (NumberFormatException e) {
+ return null;
+ }
+ }
+
+ String getLastSubtypeForInputMethodLocked(String imeId) {
+ Pair<String, String> ime = getLastSubtypeForInputMethodLockedInternal(imeId);
+ if (ime != null) {
+ return ime.second;
+ } else {
+ return null;
+ }
+ }
+
+ private Pair<String, String> getLastSubtypeForInputMethodLockedInternal(String imeId) {
+ List<Pair<String, ArrayList<String>>> enabledImes =
+ getEnabledInputMethodsAndSubtypeListLocked();
+ List<Pair<String, String>> subtypeHistory = loadInputMethodAndSubtypeHistoryLocked();
+ for (Pair<String, String> imeAndSubtype : subtypeHistory) {
+ final String imeInTheHistory = imeAndSubtype.first;
+ // If imeId is empty, returns the first IME and subtype in the history
+ if (TextUtils.isEmpty(imeId) || imeInTheHistory.equals(imeId)) {
+ final String subtypeInTheHistory = imeAndSubtype.second;
+ final String subtypeHashCode =
+ getEnabledSubtypeHashCodeForInputMethodAndSubtypeLocked(
+ enabledImes, imeInTheHistory, subtypeInTheHistory);
+ if (!TextUtils.isEmpty(subtypeHashCode)) {
+ if (DEBUG) {
+ Slog.d(TAG,
+ "Enabled subtype found in the history: " + subtypeHashCode);
+ }
+ return new Pair<>(imeInTheHistory, subtypeHashCode);
+ }
+ }
+ }
+ if (DEBUG) {
+ Slog.d(TAG, "No enabled IME found in the history");
+ }
+ return null;
+ }
+
+ private String getEnabledSubtypeHashCodeForInputMethodAndSubtypeLocked(List<Pair<String,
+ ArrayList<String>>> enabledImes, String imeId, String subtypeHashCode) {
+ final LocaleList localeList = SystemLocaleWrapper.get(mCurrentUserId);
+ for (Pair<String, ArrayList<String>> enabledIme : enabledImes) {
+ if (enabledIme.first.equals(imeId)) {
+ final ArrayList<String> explicitlyEnabledSubtypes = enabledIme.second;
+ final InputMethodInfo imi = mMethodMap.get(imeId);
+ if (explicitlyEnabledSubtypes.size() == 0) {
+ // If there are no explicitly enabled subtypes, applicable subtypes are
+ // enabled implicitly.
+ // If IME is enabled and no subtypes are enabled, applicable subtypes
+ // are enabled implicitly, so needs to treat them to be enabled.
+ if (imi != null && imi.getSubtypeCount() > 0) {
+ List<InputMethodSubtype> implicitlyEnabledSubtypes =
+ SubtypeUtils.getImplicitlyApplicableSubtypesLocked(localeList,
+ imi);
+ final int numSubtypes = implicitlyEnabledSubtypes.size();
+ for (int i = 0; i < numSubtypes; ++i) {
+ final InputMethodSubtype st = implicitlyEnabledSubtypes.get(i);
+ if (String.valueOf(st.hashCode()).equals(subtypeHashCode)) {
+ return subtypeHashCode;
+ }
+ }
+ }
+ } else {
+ for (String s : explicitlyEnabledSubtypes) {
+ if (s.equals(subtypeHashCode)) {
+ // If both imeId and subtypeId are enabled, return subtypeId.
+ try {
+ final int hashCode = Integer.parseInt(subtypeHashCode);
+ // Check whether the subtype id is valid or not
+ if (SubtypeUtils.isValidSubtypeId(imi, hashCode)) {
+ return s;
+ } else {
+ return NOT_A_SUBTYPE_ID_STR;
+ }
+ } catch (NumberFormatException e) {
+ return NOT_A_SUBTYPE_ID_STR;
+ }
+ }
+ }
+ }
+ // If imeId was enabled but subtypeId was disabled.
+ return NOT_A_SUBTYPE_ID_STR;
+ }
+ }
+ // If both imeId and subtypeId are disabled, return null
+ return null;
+ }
+
+ private List<Pair<String, String>> loadInputMethodAndSubtypeHistoryLocked() {
+ ArrayList<Pair<String, String>> imsList = new ArrayList<>();
+ final String subtypeHistoryStr = getSubtypeHistoryStr();
+ if (TextUtils.isEmpty(subtypeHistoryStr)) {
+ return imsList;
+ }
+ final TextUtils.SimpleStringSplitter inputMethodSplitter =
+ new TextUtils.SimpleStringSplitter(INPUT_METHOD_SEPARATOR);
+ final TextUtils.SimpleStringSplitter subtypeSplitter =
+ new TextUtils.SimpleStringSplitter(INPUT_METHOD_SUBTYPE_SEPARATOR);
+ inputMethodSplitter.setString(subtypeHistoryStr);
+ while (inputMethodSplitter.hasNext()) {
+ String nextImsStr = inputMethodSplitter.next();
+ subtypeSplitter.setString(nextImsStr);
+ if (subtypeSplitter.hasNext()) {
+ String subtypeId = NOT_A_SUBTYPE_ID_STR;
+ // The first element is ime id.
+ String imeId = subtypeSplitter.next();
+ while (subtypeSplitter.hasNext()) {
+ subtypeId = subtypeSplitter.next();
+ break;
+ }
+ imsList.add(new Pair<>(imeId, subtypeId));
+ }
+ }
+ return imsList;
+ }
+
+ @NonNull
+ private String getSubtypeHistoryStr() {
+ final String history = getString(Settings.Secure.INPUT_METHODS_SUBTYPE_HISTORY, "");
+ if (DEBUG) {
+ Slog.d(TAG, "getSubtypeHistoryStr: " + history);
+ }
+ return history;
+ }
+
+ void putSelectedInputMethod(String imeId) {
+ if (DEBUG) {
+ Slog.d(TAG, "putSelectedInputMethodStr: " + imeId + ", "
+ + mCurrentUserId);
+ }
+ putString(Settings.Secure.DEFAULT_INPUT_METHOD, imeId);
+ }
+
+ void putSelectedSubtype(int subtypeId) {
+ if (DEBUG) {
+ Slog.d(TAG, "putSelectedInputMethodSubtypeStr: " + subtypeId + ", "
+ + mCurrentUserId);
+ }
+ putInt(Settings.Secure.SELECTED_INPUT_METHOD_SUBTYPE, subtypeId);
+ }
+
+ @Nullable
+ String getSelectedInputMethod() {
+ final String imi = getString(Settings.Secure.DEFAULT_INPUT_METHOD, null);
+ if (DEBUG) {
+ Slog.d(TAG, "getSelectedInputMethodStr: " + imi);
+ }
+ return imi;
+ }
+
+ @Nullable
+ String getSelectedDefaultDeviceInputMethod() {
+ final String imi = getString(Settings.Secure.DEFAULT_DEVICE_INPUT_METHOD, null);
+ if (DEBUG) {
+ Slog.d(TAG, "getSelectedDefaultDeviceInputMethodStr: " + imi + ", "
+ + mCurrentUserId);
+ }
+ return imi;
+ }
+
+ void putSelectedDefaultDeviceInputMethod(String imeId) {
+ if (DEBUG) {
+ Slog.d(TAG, "putSelectedDefaultDeviceInputMethodStr: " + imeId + ", "
+ + mCurrentUserId);
+ }
+ putString(Settings.Secure.DEFAULT_DEVICE_INPUT_METHOD, imeId);
+ }
+
+ void putDefaultVoiceInputMethod(String imeId) {
+ if (DEBUG) {
+ Slog.d(TAG,
+ "putDefaultVoiceInputMethodStr: " + imeId + ", " + mCurrentUserId);
+ }
+ putString(Settings.Secure.DEFAULT_VOICE_INPUT_METHOD, imeId);
+ }
+
+ @Nullable
+ String getDefaultVoiceInputMethod() {
+ final String imi = getString(Settings.Secure.DEFAULT_VOICE_INPUT_METHOD, null);
+ if (DEBUG) {
+ Slog.d(TAG, "getDefaultVoiceInputMethodStr: " + imi);
+ }
+ return imi;
+ }
+
+ boolean isSubtypeSelected() {
+ return getSelectedInputMethodSubtypeHashCode() != NOT_A_SUBTYPE_ID;
+ }
+
+ private int getSelectedInputMethodSubtypeHashCode() {
+ return getInt(Settings.Secure.SELECTED_INPUT_METHOD_SUBTYPE,
+ NOT_A_SUBTYPE_ID);
+ }
+
+ @UserIdInt
+ public int getCurrentUserId() {
+ return mCurrentUserId;
+ }
+
+ int getSelectedInputMethodSubtypeId(String selectedImiId) {
+ final InputMethodInfo imi = mMethodMap.get(selectedImiId);
+ if (imi == null) {
+ return NOT_A_SUBTYPE_ID;
+ }
+ final int subtypeHashCode = getSelectedInputMethodSubtypeHashCode();
+ return SubtypeUtils.getSubtypeIdFromHashCode(imi, subtypeHashCode);
+ }
+
+ void saveCurrentInputMethodAndSubtypeToHistory(String curMethodId,
+ InputMethodSubtype currentSubtype) {
+ String subtypeId = NOT_A_SUBTYPE_ID_STR;
+ if (currentSubtype != null) {
+ subtypeId = String.valueOf(currentSubtype.hashCode());
+ }
+ if (InputMethodUtils.canAddToLastInputMethod(currentSubtype)) {
+ addSubtypeToHistory(curMethodId, subtypeId);
+ }
+ }
+
+ /**
+ * A variant of {@link InputMethodManagerService#getCurrentInputMethodSubtypeLocked()} for
+ * non-current users.
+ *
+ * <p>TODO: Address code duplication between this and
+ * {@link InputMethodManagerService#getCurrentInputMethodSubtypeLocked()}.</p>
+ *
+ * @return {@link InputMethodSubtype} if exists. {@code null} otherwise.
+ */
+ @Nullable
+ InputMethodSubtype getCurrentInputMethodSubtypeForNonCurrentUsers() {
+ final String selectedMethodId = getSelectedInputMethod();
+ if (selectedMethodId == null) {
+ return null;
+ }
+ final InputMethodInfo imi = mMethodMap.get(selectedMethodId);
+ if (imi == null || imi.getSubtypeCount() == 0) {
+ return null;
+ }
+
+ final int subtypeHashCode = getSelectedInputMethodSubtypeHashCode();
+ if (subtypeHashCode != NOT_A_SUBTYPE_ID) {
+ final int subtypeIndex = SubtypeUtils.getSubtypeIdFromHashCode(imi,
+ subtypeHashCode);
+ if (subtypeIndex >= 0) {
+ return imi.getSubtypeAt(subtypeIndex);
+ }
+ }
+
+ // If there are no selected subtypes, the framework will try to find the most applicable
+ // subtype from explicitly or implicitly enabled subtypes.
+ final List<InputMethodSubtype> explicitlyOrImplicitlyEnabledSubtypes =
+ getEnabledInputMethodSubtypeListLocked(imi, true);
+ // If there is only one explicitly or implicitly enabled subtype, just returns it.
+ if (explicitlyOrImplicitlyEnabledSubtypes.isEmpty()) {
+ return null;
+ }
+ if (explicitlyOrImplicitlyEnabledSubtypes.size() == 1) {
+ return explicitlyOrImplicitlyEnabledSubtypes.get(0);
+ }
+ final String locale = SystemLocaleWrapper.get(mCurrentUserId).get(0).toString();
+ final InputMethodSubtype subtype = SubtypeUtils.findLastResortApplicableSubtypeLocked(
+ explicitlyOrImplicitlyEnabledSubtypes, SubtypeUtils.SUBTYPE_MODE_KEYBOARD,
+ locale, true);
+ if (subtype != null) {
+ return subtype;
+ }
+ return SubtypeUtils.findLastResortApplicableSubtypeLocked(
+ explicitlyOrImplicitlyEnabledSubtypes, null, locale, true);
+ }
+
+ boolean setAdditionalInputMethodSubtypes(@NonNull String imeId,
+ @NonNull ArrayList<InputMethodSubtype> subtypes,
+ @NonNull ArrayMap<String, List<InputMethodSubtype>> additionalSubtypeMap,
+ @NonNull PackageManagerInternal packageManagerInternal, int callingUid) {
+ final InputMethodInfo imi = mMethodMap.get(imeId);
+ if (imi == null) {
+ return false;
+ }
+ if (!InputMethodUtils.checkIfPackageBelongsToUid(packageManagerInternal, callingUid,
+ imi.getPackageName())) {
+ return false;
+ }
+
+ if (subtypes.isEmpty()) {
+ additionalSubtypeMap.remove(imi.getId());
+ } else {
+ additionalSubtypeMap.put(imi.getId(), subtypes);
+ }
+ AdditionalSubtypeUtils.save(additionalSubtypeMap, mMethodMap, getCurrentUserId());
+ return true;
+ }
+
+ boolean setEnabledInputMethodSubtypes(@NonNull String imeId,
+ @NonNull int[] subtypeHashCodes) {
+ final InputMethodInfo imi = mMethodMap.get(imeId);
+ if (imi == null) {
+ return false;
+ }
+
+ final IntArray validSubtypeHashCodes = new IntArray(subtypeHashCodes.length);
+ for (int subtypeHashCode : subtypeHashCodes) {
+ if (subtypeHashCode == NOT_A_SUBTYPE_ID) {
+ continue; // NOT_A_SUBTYPE_ID must not be saved
+ }
+ if (!SubtypeUtils.isValidSubtypeId(imi, subtypeHashCode)) {
+ continue; // this subtype does not exist in InputMethodInfo.
+ }
+ if (validSubtypeHashCodes.indexOf(subtypeHashCode) >= 0) {
+ continue; // The entry is already added. No need to add anymore.
+ }
+ validSubtypeHashCodes.add(subtypeHashCode);
+ }
+
+ final String originalEnabledImesString = getEnabledInputMethodsStr();
+ final String updatedEnabledImesString = updateEnabledImeString(
+ originalEnabledImesString, imi.getId(), validSubtypeHashCodes);
+ if (TextUtils.equals(originalEnabledImesString, updatedEnabledImesString)) {
+ return false;
+ }
+
+ putEnabledInputMethodsStr(updatedEnabledImesString);
+ return true;
+ }
+
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+ static String updateEnabledImeString(@NonNull String enabledImesString,
+ @NonNull String imeId, @NonNull IntArray enabledSubtypeHashCodes) {
+ final TextUtils.SimpleStringSplitter imeSplitter =
+ new TextUtils.SimpleStringSplitter(INPUT_METHOD_SEPARATOR);
+ final TextUtils.SimpleStringSplitter imeSubtypeSplitter =
+ new TextUtils.SimpleStringSplitter(INPUT_METHOD_SUBTYPE_SEPARATOR);
+
+ final StringBuilder sb = new StringBuilder();
+
+ imeSplitter.setString(enabledImesString);
+ boolean needsImeSeparator = false;
+ while (imeSplitter.hasNext()) {
+ final String nextImsStr = imeSplitter.next();
+ imeSubtypeSplitter.setString(nextImsStr);
+ if (imeSubtypeSplitter.hasNext()) {
+ if (needsImeSeparator) {
+ sb.append(INPUT_METHOD_SEPARATOR);
+ }
+ if (TextUtils.equals(imeId, imeSubtypeSplitter.next())) {
+ sb.append(imeId);
+ for (int i = 0; i < enabledSubtypeHashCodes.size(); ++i) {
+ sb.append(INPUT_METHOD_SUBTYPE_SEPARATOR);
+ sb.append(enabledSubtypeHashCodes.get(i));
+ }
+ } else {
+ sb.append(nextImsStr);
+ }
+ needsImeSeparator = true;
+ }
+ }
+ return sb.toString();
+ }
+
+ void dumpLocked(final Printer pw, final String prefix) {
+ pw.println(prefix + "mCurrentUserId=" + mCurrentUserId);
+ }
+}
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodSubtypeSwitchingController.java b/services/core/java/com/android/server/inputmethod/InputMethodSubtypeSwitchingController.java
index 4439b06..4305808 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodSubtypeSwitchingController.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodSubtypeSwitchingController.java
@@ -31,7 +31,6 @@
import android.view.inputmethod.InputMethodSubtype;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.server.inputmethod.InputMethodUtils.InputMethodSettings;
import java.util.ArrayList;
import java.util.Collections;
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodUtils.java b/services/core/java/com/android/server/inputmethod/InputMethodUtils.java
index 58a68f2a..361cdbb 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodUtils.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodUtils.java
@@ -28,15 +28,10 @@
import android.content.pm.ResolveInfo;
import android.content.res.Resources;
import android.os.Build;
-import android.os.LocaleList;
import android.os.UserHandle;
import android.provider.Settings;
import android.text.TextUtils;
-import android.util.ArrayMap;
import android.util.ArraySet;
-import android.util.IntArray;
-import android.util.Pair;
-import android.util.Printer;
import android.util.Slog;
import android.view.inputmethod.InputMethodInfo;
import android.view.inputmethod.InputMethodSubtype;
@@ -51,10 +46,8 @@
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
-import java.util.Objects;
import java.util.StringJoiner;
import java.util.function.Consumer;
-import java.util.function.Predicate;
/**
* This class provides random static utility methods for {@link InputMethodManagerService} and its
@@ -68,12 +61,11 @@
public static final boolean DEBUG = false;
static final int NOT_A_SUBTYPE_ID = -1;
private static final String TAG = "InputMethodUtils";
- private static final String NOT_A_SUBTYPE_ID_STR = String.valueOf(NOT_A_SUBTYPE_ID);
// The string for enabled input method is saved as follows:
// example: ("ime0;subtype0;subtype1;subtype2:ime1:ime2;subtype0")
- private static final char INPUT_METHOD_SEPARATOR = ':';
- private static final char INPUT_METHOD_SUBTYPE_SEPARATOR = ';';
+ static final char INPUT_METHOD_SEPARATOR = ':';
+ static final char INPUT_METHOD_SUBTYPE_SEPARATOR = ';';
private InputMethodUtils() {
// This utility class is not publicly instantiable.
@@ -200,640 +192,6 @@
UserHandle.getUserId(uid));
}
- /**
- * Utility class for putting and getting settings for InputMethod.
- *
- * This is used in two ways:
- * - Singleton instance in {@link InputMethodManagerService}, which is updated on user-switch to
- * follow the current user.
- * - On-demand instances when we need settings for non-current users.
- *
- * TODO: Move all putters and getters of settings to this class.
- */
- @UserHandleAware
- public static class InputMethodSettings {
- private final ArrayMap<String, InputMethodInfo> mMethodMap;
-
- @UserIdInt
- private final int mCurrentUserId;
-
- private static void buildEnabledInputMethodsSettingString(
- StringBuilder builder, Pair<String, ArrayList<String>> ime) {
- builder.append(ime.first);
- // Inputmethod and subtypes are saved in the settings as follows:
- // ime0;subtype0;subtype1:ime1;subtype0:ime2:ime3;subtype0;subtype1
- for (String subtypeId: ime.second) {
- builder.append(INPUT_METHOD_SUBTYPE_SEPARATOR).append(subtypeId);
- }
- }
-
- InputMethodSettings(ArrayMap<String, InputMethodInfo> methodMap, @UserIdInt int userId) {
- mMethodMap = methodMap;
- mCurrentUserId = userId;
- String ime = getSelectedInputMethod();
- String defaultDeviceIme = getSelectedDefaultDeviceInputMethod();
- if (defaultDeviceIme != null && !Objects.equals(ime, defaultDeviceIme)) {
- putSelectedInputMethod(defaultDeviceIme);
- putSelectedDefaultDeviceInputMethod(null);
- }
- }
-
- private void putString(@NonNull String key, @Nullable String str) {
- SecureSettingsWrapper.putString(key, str, mCurrentUserId);
- }
-
- @Nullable
- private String getString(@NonNull String key, @Nullable String defaultValue) {
- return SecureSettingsWrapper.getString(key, defaultValue, mCurrentUserId);
- }
-
- private void putInt(String key, int value) {
- SecureSettingsWrapper.putInt(key, value, mCurrentUserId);
- }
-
- private int getInt(String key, int defaultValue) {
- return SecureSettingsWrapper.getInt(key, defaultValue, mCurrentUserId);
- }
-
- ArrayList<InputMethodInfo> getEnabledInputMethodListLocked() {
- return getEnabledInputMethodListWithFilterLocked(null /* matchingCondition */);
- }
-
- @NonNull
- ArrayList<InputMethodInfo> getEnabledInputMethodListWithFilterLocked(
- @Nullable Predicate<InputMethodInfo> matchingCondition) {
- return createEnabledInputMethodListLocked(
- getEnabledInputMethodsAndSubtypeListLocked(), matchingCondition);
- }
-
- List<InputMethodSubtype> getEnabledInputMethodSubtypeListLocked(
- InputMethodInfo imi, boolean allowsImplicitlyEnabledSubtypes) {
- List<InputMethodSubtype> enabledSubtypes =
- getEnabledInputMethodSubtypeListLocked(imi);
- if (allowsImplicitlyEnabledSubtypes && enabledSubtypes.isEmpty()) {
- enabledSubtypes = SubtypeUtils.getImplicitlyApplicableSubtypesLocked(
- SystemLocaleWrapper.get(mCurrentUserId), imi);
- }
- return InputMethodSubtype.sort(imi, enabledSubtypes);
- }
-
- List<InputMethodSubtype> getEnabledInputMethodSubtypeListLocked(InputMethodInfo imi) {
- List<Pair<String, ArrayList<String>>> imsList =
- getEnabledInputMethodsAndSubtypeListLocked();
- ArrayList<InputMethodSubtype> enabledSubtypes = new ArrayList<>();
- if (imi != null) {
- for (Pair<String, ArrayList<String>> imsPair : imsList) {
- InputMethodInfo info = mMethodMap.get(imsPair.first);
- if (info != null && info.getId().equals(imi.getId())) {
- final int subtypeCount = info.getSubtypeCount();
- for (int i = 0; i < subtypeCount; ++i) {
- InputMethodSubtype ims = info.getSubtypeAt(i);
- for (String s: imsPair.second) {
- if (String.valueOf(ims.hashCode()).equals(s)) {
- enabledSubtypes.add(ims);
- }
- }
- }
- break;
- }
- }
- }
- return enabledSubtypes;
- }
-
- List<Pair<String, ArrayList<String>>> getEnabledInputMethodsAndSubtypeListLocked() {
- final String enabledInputMethodsStr = getEnabledInputMethodsStr();
- final TextUtils.SimpleStringSplitter inputMethodSplitter =
- new TextUtils.SimpleStringSplitter(INPUT_METHOD_SEPARATOR);
- final TextUtils.SimpleStringSplitter subtypeSplitter =
- new TextUtils.SimpleStringSplitter(INPUT_METHOD_SUBTYPE_SEPARATOR);
- final ArrayList<Pair<String, ArrayList<String>>> imsList = new ArrayList<>();
- if (TextUtils.isEmpty(enabledInputMethodsStr)) {
- return imsList;
- }
- inputMethodSplitter.setString(enabledInputMethodsStr);
- while (inputMethodSplitter.hasNext()) {
- String nextImsStr = inputMethodSplitter.next();
- subtypeSplitter.setString(nextImsStr);
- if (subtypeSplitter.hasNext()) {
- ArrayList<String> subtypeHashes = new ArrayList<>();
- // The first element is ime id.
- String imeId = subtypeSplitter.next();
- while (subtypeSplitter.hasNext()) {
- subtypeHashes.add(subtypeSplitter.next());
- }
- imsList.add(new Pair<>(imeId, subtypeHashes));
- }
- }
- return imsList;
- }
-
- /**
- * Build and put a string of EnabledInputMethods with removing specified Id.
- * @return the specified id was removed or not.
- */
- boolean buildAndPutEnabledInputMethodsStrRemovingIdLocked(
- StringBuilder builder, List<Pair<String, ArrayList<String>>> imsList, String id) {
- boolean isRemoved = false;
- boolean needsAppendSeparator = false;
- for (Pair<String, ArrayList<String>> ims: imsList) {
- String curId = ims.first;
- if (curId.equals(id)) {
- // We are disabling this input method, and it is
- // currently enabled. Skip it to remove from the
- // new list.
- isRemoved = true;
- } else {
- if (needsAppendSeparator) {
- builder.append(INPUT_METHOD_SEPARATOR);
- } else {
- needsAppendSeparator = true;
- }
- buildEnabledInputMethodsSettingString(builder, ims);
- }
- }
- if (isRemoved) {
- // Update the setting with the new list of input methods.
- putEnabledInputMethodsStr(builder.toString());
- }
- return isRemoved;
- }
-
- private ArrayList<InputMethodInfo> createEnabledInputMethodListLocked(
- List<Pair<String, ArrayList<String>>> imsList,
- Predicate<InputMethodInfo> matchingCondition) {
- final ArrayList<InputMethodInfo> res = new ArrayList<>();
- for (Pair<String, ArrayList<String>> ims: imsList) {
- InputMethodInfo info = mMethodMap.get(ims.first);
- if (info != null && !info.isVrOnly()
- && (matchingCondition == null || matchingCondition.test(info))) {
- res.add(info);
- }
- }
- return res;
- }
-
- void putEnabledInputMethodsStr(@Nullable String str) {
- if (DEBUG) {
- Slog.d(TAG, "putEnabledInputMethodStr: " + str);
- }
- if (TextUtils.isEmpty(str)) {
- // OK to coalesce to null, since getEnabledInputMethodsStr() can take care of the
- // empty data scenario.
- putString(Settings.Secure.ENABLED_INPUT_METHODS, null);
- } else {
- putString(Settings.Secure.ENABLED_INPUT_METHODS, str);
- }
- }
-
- @NonNull
- String getEnabledInputMethodsStr() {
- return getString(Settings.Secure.ENABLED_INPUT_METHODS, "");
- }
-
- private void saveSubtypeHistory(
- List<Pair<String, String>> savedImes, String newImeId, String newSubtypeId) {
- StringBuilder builder = new StringBuilder();
- boolean isImeAdded = false;
- if (!TextUtils.isEmpty(newImeId) && !TextUtils.isEmpty(newSubtypeId)) {
- builder.append(newImeId).append(INPUT_METHOD_SUBTYPE_SEPARATOR).append(
- newSubtypeId);
- isImeAdded = true;
- }
- for (Pair<String, String> ime: savedImes) {
- String imeId = ime.first;
- String subtypeId = ime.second;
- if (TextUtils.isEmpty(subtypeId)) {
- subtypeId = NOT_A_SUBTYPE_ID_STR;
- }
- if (isImeAdded) {
- builder.append(INPUT_METHOD_SEPARATOR);
- } else {
- isImeAdded = true;
- }
- builder.append(imeId).append(INPUT_METHOD_SUBTYPE_SEPARATOR).append(
- subtypeId);
- }
- // Remove the last INPUT_METHOD_SEPARATOR
- putSubtypeHistoryStr(builder.toString());
- }
-
- private void addSubtypeToHistory(String imeId, String subtypeId) {
- List<Pair<String, String>> subtypeHistory = loadInputMethodAndSubtypeHistoryLocked();
- for (Pair<String, String> ime: subtypeHistory) {
- if (ime.first.equals(imeId)) {
- if (DEBUG) {
- Slog.v(TAG, "Subtype found in the history: " + imeId + ", "
- + ime.second);
- }
- // We should break here
- subtypeHistory.remove(ime);
- break;
- }
- }
- if (DEBUG) {
- Slog.v(TAG, "Add subtype to the history: " + imeId + ", " + subtypeId);
- }
- saveSubtypeHistory(subtypeHistory, imeId, subtypeId);
- }
-
- private void putSubtypeHistoryStr(@NonNull String str) {
- if (DEBUG) {
- Slog.d(TAG, "putSubtypeHistoryStr: " + str);
- }
- if (TextUtils.isEmpty(str)) {
- // OK to coalesce to null, since getSubtypeHistoryStr() can take care of the empty
- // data scenario.
- putString(Settings.Secure.INPUT_METHODS_SUBTYPE_HISTORY, null);
- } else {
- putString(Settings.Secure.INPUT_METHODS_SUBTYPE_HISTORY, str);
- }
- }
-
- Pair<String, String> getLastInputMethodAndSubtypeLocked() {
- // Gets the first one from the history
- return getLastSubtypeForInputMethodLockedInternal(null);
- }
-
- @Nullable
- InputMethodSubtype getLastInputMethodSubtypeLocked() {
- final Pair<String, String> lastIme = getLastInputMethodAndSubtypeLocked();
- // TODO: Handle the case of the last IME with no subtypes
- if (lastIme == null || TextUtils.isEmpty(lastIme.first)
- || TextUtils.isEmpty(lastIme.second)) return null;
- final InputMethodInfo lastImi = mMethodMap.get(lastIme.first);
- if (lastImi == null) return null;
- try {
- final int lastSubtypeHash = Integer.parseInt(lastIme.second);
- final int lastSubtypeId = SubtypeUtils.getSubtypeIdFromHashCode(lastImi,
- lastSubtypeHash);
- if (lastSubtypeId < 0 || lastSubtypeId >= lastImi.getSubtypeCount()) {
- return null;
- }
- return lastImi.getSubtypeAt(lastSubtypeId);
- } catch (NumberFormatException e) {
- return null;
- }
- }
-
- String getLastSubtypeForInputMethodLocked(String imeId) {
- Pair<String, String> ime = getLastSubtypeForInputMethodLockedInternal(imeId);
- if (ime != null) {
- return ime.second;
- } else {
- return null;
- }
- }
-
- private Pair<String, String> getLastSubtypeForInputMethodLockedInternal(String imeId) {
- List<Pair<String, ArrayList<String>>> enabledImes =
- getEnabledInputMethodsAndSubtypeListLocked();
- List<Pair<String, String>> subtypeHistory = loadInputMethodAndSubtypeHistoryLocked();
- for (Pair<String, String> imeAndSubtype : subtypeHistory) {
- final String imeInTheHistory = imeAndSubtype.first;
- // If imeId is empty, returns the first IME and subtype in the history
- if (TextUtils.isEmpty(imeId) || imeInTheHistory.equals(imeId)) {
- final String subtypeInTheHistory = imeAndSubtype.second;
- final String subtypeHashCode =
- getEnabledSubtypeHashCodeForInputMethodAndSubtypeLocked(
- enabledImes, imeInTheHistory, subtypeInTheHistory);
- if (!TextUtils.isEmpty(subtypeHashCode)) {
- if (DEBUG) {
- Slog.d(TAG, "Enabled subtype found in the history: " + subtypeHashCode);
- }
- return new Pair<>(imeInTheHistory, subtypeHashCode);
- }
- }
- }
- if (DEBUG) {
- Slog.d(TAG, "No enabled IME found in the history");
- }
- return null;
- }
-
- private String getEnabledSubtypeHashCodeForInputMethodAndSubtypeLocked(List<Pair<String,
- ArrayList<String>>> enabledImes, String imeId, String subtypeHashCode) {
- final LocaleList localeList = SystemLocaleWrapper.get(mCurrentUserId);
- for (Pair<String, ArrayList<String>> enabledIme: enabledImes) {
- if (enabledIme.first.equals(imeId)) {
- final ArrayList<String> explicitlyEnabledSubtypes = enabledIme.second;
- final InputMethodInfo imi = mMethodMap.get(imeId);
- if (explicitlyEnabledSubtypes.size() == 0) {
- // If there are no explicitly enabled subtypes, applicable subtypes are
- // enabled implicitly.
- // If IME is enabled and no subtypes are enabled, applicable subtypes
- // are enabled implicitly, so needs to treat them to be enabled.
- if (imi != null && imi.getSubtypeCount() > 0) {
- List<InputMethodSubtype> implicitlyEnabledSubtypes =
- SubtypeUtils.getImplicitlyApplicableSubtypesLocked(localeList,
- imi);
- final int numSubtypes = implicitlyEnabledSubtypes.size();
- for (int i = 0; i < numSubtypes; ++i) {
- final InputMethodSubtype st = implicitlyEnabledSubtypes.get(i);
- if (String.valueOf(st.hashCode()).equals(subtypeHashCode)) {
- return subtypeHashCode;
- }
- }
- }
- } else {
- for (String s: explicitlyEnabledSubtypes) {
- if (s.equals(subtypeHashCode)) {
- // If both imeId and subtypeId are enabled, return subtypeId.
- try {
- final int hashCode = Integer.parseInt(subtypeHashCode);
- // Check whether the subtype id is valid or not
- if (SubtypeUtils.isValidSubtypeId(imi, hashCode)) {
- return s;
- } else {
- return NOT_A_SUBTYPE_ID_STR;
- }
- } catch (NumberFormatException e) {
- return NOT_A_SUBTYPE_ID_STR;
- }
- }
- }
- }
- // If imeId was enabled but subtypeId was disabled.
- return NOT_A_SUBTYPE_ID_STR;
- }
- }
- // If both imeId and subtypeId are disabled, return null
- return null;
- }
-
- private List<Pair<String, String>> loadInputMethodAndSubtypeHistoryLocked() {
- ArrayList<Pair<String, String>> imsList = new ArrayList<>();
- final String subtypeHistoryStr = getSubtypeHistoryStr();
- if (TextUtils.isEmpty(subtypeHistoryStr)) {
- return imsList;
- }
- final TextUtils.SimpleStringSplitter inputMethodSplitter =
- new TextUtils.SimpleStringSplitter(INPUT_METHOD_SEPARATOR);
- final TextUtils.SimpleStringSplitter subtypeSplitter =
- new TextUtils.SimpleStringSplitter(INPUT_METHOD_SUBTYPE_SEPARATOR);
- inputMethodSplitter.setString(subtypeHistoryStr);
- while (inputMethodSplitter.hasNext()) {
- String nextImsStr = inputMethodSplitter.next();
- subtypeSplitter.setString(nextImsStr);
- if (subtypeSplitter.hasNext()) {
- String subtypeId = NOT_A_SUBTYPE_ID_STR;
- // The first element is ime id.
- String imeId = subtypeSplitter.next();
- while (subtypeSplitter.hasNext()) {
- subtypeId = subtypeSplitter.next();
- break;
- }
- imsList.add(new Pair<>(imeId, subtypeId));
- }
- }
- return imsList;
- }
-
- @NonNull
- private String getSubtypeHistoryStr() {
- final String history = getString(Settings.Secure.INPUT_METHODS_SUBTYPE_HISTORY, "");
- if (DEBUG) {
- Slog.d(TAG, "getSubtypeHistoryStr: " + history);
- }
- return history;
- }
-
- void putSelectedInputMethod(String imeId) {
- if (DEBUG) {
- Slog.d(TAG, "putSelectedInputMethodStr: " + imeId + ", "
- + mCurrentUserId);
- }
- putString(Settings.Secure.DEFAULT_INPUT_METHOD, imeId);
- }
-
- void putSelectedSubtype(int subtypeId) {
- if (DEBUG) {
- Slog.d(TAG, "putSelectedInputMethodSubtypeStr: " + subtypeId + ", "
- + mCurrentUserId);
- }
- putInt(Settings.Secure.SELECTED_INPUT_METHOD_SUBTYPE, subtypeId);
- }
-
- @Nullable
- String getSelectedInputMethod() {
- final String imi = getString(Settings.Secure.DEFAULT_INPUT_METHOD, null);
- if (DEBUG) {
- Slog.d(TAG, "getSelectedInputMethodStr: " + imi);
- }
- return imi;
- }
-
- @Nullable
- String getSelectedDefaultDeviceInputMethod() {
- final String imi = getString(Settings.Secure.DEFAULT_DEVICE_INPUT_METHOD, null);
- if (DEBUG) {
- Slog.d(TAG, "getSelectedDefaultDeviceInputMethodStr: " + imi + ", "
- + mCurrentUserId);
- }
- return imi;
- }
-
- void putSelectedDefaultDeviceInputMethod(String imeId) {
- if (DEBUG) {
- Slog.d(TAG, "putSelectedDefaultDeviceInputMethodStr: " + imeId + ", "
- + mCurrentUserId);
- }
- putString(Settings.Secure.DEFAULT_DEVICE_INPUT_METHOD, imeId);
- }
-
- void putDefaultVoiceInputMethod(String imeId) {
- if (DEBUG) {
- Slog.d(TAG, "putDefaultVoiceInputMethodStr: " + imeId + ", " + mCurrentUserId);
- }
- putString(Settings.Secure.DEFAULT_VOICE_INPUT_METHOD, imeId);
- }
-
- @Nullable
- String getDefaultVoiceInputMethod() {
- final String imi = getString(Settings.Secure.DEFAULT_VOICE_INPUT_METHOD, null);
- if (DEBUG) {
- Slog.d(TAG, "getDefaultVoiceInputMethodStr: " + imi);
- }
- return imi;
- }
-
- boolean isSubtypeSelected() {
- return getSelectedInputMethodSubtypeHashCode() != NOT_A_SUBTYPE_ID;
- }
-
- private int getSelectedInputMethodSubtypeHashCode() {
- return getInt(Settings.Secure.SELECTED_INPUT_METHOD_SUBTYPE, NOT_A_SUBTYPE_ID);
- }
-
- @UserIdInt
- public int getCurrentUserId() {
- return mCurrentUserId;
- }
-
- int getSelectedInputMethodSubtypeId(String selectedImiId) {
- final InputMethodInfo imi = mMethodMap.get(selectedImiId);
- if (imi == null) {
- return NOT_A_SUBTYPE_ID;
- }
- final int subtypeHashCode = getSelectedInputMethodSubtypeHashCode();
- return SubtypeUtils.getSubtypeIdFromHashCode(imi, subtypeHashCode);
- }
-
- void saveCurrentInputMethodAndSubtypeToHistory(String curMethodId,
- InputMethodSubtype currentSubtype) {
- String subtypeId = NOT_A_SUBTYPE_ID_STR;
- if (currentSubtype != null) {
- subtypeId = String.valueOf(currentSubtype.hashCode());
- }
- if (canAddToLastInputMethod(currentSubtype)) {
- addSubtypeToHistory(curMethodId, subtypeId);
- }
- }
-
- /**
- * A variant of {@link InputMethodManagerService#getCurrentInputMethodSubtypeLocked()} for
- * non-current users.
- *
- * <p>TODO: Address code duplication between this and
- * {@link InputMethodManagerService#getCurrentInputMethodSubtypeLocked()}.</p>
- *
- * @return {@link InputMethodSubtype} if exists. {@code null} otherwise.
- */
- @Nullable
- InputMethodSubtype getCurrentInputMethodSubtypeForNonCurrentUsers() {
- final String selectedMethodId = getSelectedInputMethod();
- if (selectedMethodId == null) {
- return null;
- }
- final InputMethodInfo imi = mMethodMap.get(selectedMethodId);
- if (imi == null || imi.getSubtypeCount() == 0) {
- return null;
- }
-
- final int subtypeHashCode = getSelectedInputMethodSubtypeHashCode();
- if (subtypeHashCode != InputMethodUtils.NOT_A_SUBTYPE_ID) {
- final int subtypeIndex = SubtypeUtils.getSubtypeIdFromHashCode(imi,
- subtypeHashCode);
- if (subtypeIndex >= 0) {
- return imi.getSubtypeAt(subtypeIndex);
- }
- }
-
- // If there are no selected subtypes, the framework will try to find the most applicable
- // subtype from explicitly or implicitly enabled subtypes.
- final List<InputMethodSubtype> explicitlyOrImplicitlyEnabledSubtypes =
- getEnabledInputMethodSubtypeListLocked(imi, true);
- // If there is only one explicitly or implicitly enabled subtype, just returns it.
- if (explicitlyOrImplicitlyEnabledSubtypes.isEmpty()) {
- return null;
- }
- if (explicitlyOrImplicitlyEnabledSubtypes.size() == 1) {
- return explicitlyOrImplicitlyEnabledSubtypes.get(0);
- }
- final String locale = SystemLocaleWrapper.get(mCurrentUserId).get(0).toString();
- final InputMethodSubtype subtype = SubtypeUtils.findLastResortApplicableSubtypeLocked(
- explicitlyOrImplicitlyEnabledSubtypes, SubtypeUtils.SUBTYPE_MODE_KEYBOARD,
- locale, true);
- if (subtype != null) {
- return subtype;
- }
- return SubtypeUtils.findLastResortApplicableSubtypeLocked(
- explicitlyOrImplicitlyEnabledSubtypes, null, locale, true);
- }
-
- boolean setAdditionalInputMethodSubtypes(@NonNull String imeId,
- @NonNull ArrayList<InputMethodSubtype> subtypes,
- @NonNull ArrayMap<String, List<InputMethodSubtype>> additionalSubtypeMap,
- @NonNull PackageManagerInternal packageManagerInternal, int callingUid) {
- final InputMethodInfo imi = mMethodMap.get(imeId);
- if (imi == null) {
- return false;
- }
- if (!InputMethodUtils.checkIfPackageBelongsToUid(packageManagerInternal, callingUid,
- imi.getPackageName())) {
- return false;
- }
-
- if (subtypes.isEmpty()) {
- additionalSubtypeMap.remove(imi.getId());
- } else {
- additionalSubtypeMap.put(imi.getId(), subtypes);
- }
- AdditionalSubtypeUtils.save(additionalSubtypeMap, mMethodMap, getCurrentUserId());
- return true;
- }
-
- boolean setEnabledInputMethodSubtypes(@NonNull String imeId,
- @NonNull int[] subtypeHashCodes) {
- final InputMethodInfo imi = mMethodMap.get(imeId);
- if (imi == null) {
- return false;
- }
-
- final IntArray validSubtypeHashCodes = new IntArray(subtypeHashCodes.length);
- for (int subtypeHashCode : subtypeHashCodes) {
- if (subtypeHashCode == NOT_A_SUBTYPE_ID) {
- continue; // NOT_A_SUBTYPE_ID must not be saved
- }
- if (!SubtypeUtils.isValidSubtypeId(imi, subtypeHashCode)) {
- continue; // this subtype does not exist in InputMethodInfo.
- }
- if (validSubtypeHashCodes.indexOf(subtypeHashCode) >= 0) {
- continue; // The entry is already added. No need to add anymore.
- }
- validSubtypeHashCodes.add(subtypeHashCode);
- }
-
- final String originalEnabledImesString = getEnabledInputMethodsStr();
- final String updatedEnabledImesString = updateEnabledImeString(
- originalEnabledImesString, imi.getId(), validSubtypeHashCodes);
- if (TextUtils.equals(originalEnabledImesString, updatedEnabledImesString)) {
- return false;
- }
-
- putEnabledInputMethodsStr(updatedEnabledImesString);
- return true;
- }
-
- @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
- static String updateEnabledImeString(@NonNull String enabledImesString,
- @NonNull String imeId, @NonNull IntArray enabledSubtypeHashCodes) {
- final TextUtils.SimpleStringSplitter imeSplitter =
- new TextUtils.SimpleStringSplitter(INPUT_METHOD_SEPARATOR);
- final TextUtils.SimpleStringSplitter imeSubtypeSplitter =
- new TextUtils.SimpleStringSplitter(INPUT_METHOD_SUBTYPE_SEPARATOR);
-
- final StringBuilder sb = new StringBuilder();
-
- imeSplitter.setString(enabledImesString);
- boolean needsImeSeparator = false;
- while (imeSplitter.hasNext()) {
- final String nextImsStr = imeSplitter.next();
- imeSubtypeSplitter.setString(nextImsStr);
- if (imeSubtypeSplitter.hasNext()) {
- if (needsImeSeparator) {
- sb.append(INPUT_METHOD_SEPARATOR);
- }
- if (TextUtils.equals(imeId, imeSubtypeSplitter.next())) {
- sb.append(imeId);
- for (int i = 0; i < enabledSubtypeHashCodes.size(); ++i) {
- sb.append(INPUT_METHOD_SUBTYPE_SEPARATOR);
- sb.append(enabledSubtypeHashCodes.get(i));
- }
- } else {
- sb.append(nextImsStr);
- }
- needsImeSeparator = true;
- }
- }
- return sb.toString();
- }
-
- public void dumpLocked(final Printer pw, final String prefix) {
- pw.println(prefix + "mCurrentUserId=" + mCurrentUserId);
- }
- }
-
static boolean isSoftInputModeStateVisibleAllowed(int targetSdkVersion,
@StartInputFlags int startInputFlags) {
if (targetSdkVersion < Build.VERSION_CODES.P) {
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/log/BiometricContextProviderTest.java b/services/tests/servicestests/src/com/android/server/biometrics/log/BiometricContextProviderTest.java
index 1b9e6fb..a8eace0 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/log/BiometricContextProviderTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/log/BiometricContextProviderTest.java
@@ -153,6 +153,15 @@
}
@Test
+ public void testGetIsHardwareIgnoringTouches() throws RemoteException {
+ mListener.onHardwareIgnoreTouchesChanged(true);
+ assertThat(mProvider.isHardwareIgnoringTouches()).isTrue();
+
+ mListener.onHardwareIgnoreTouchesChanged(false);
+ assertThat(mProvider.isHardwareIgnoringTouches()).isFalse();
+ }
+
+ @Test
public void testGetDockedState() {
final List<Integer> states = List.of(Intent.EXTRA_DOCK_STATE_DESK,
Intent.EXTRA_DOCK_STATE_CAR, Intent.EXTRA_DOCK_STATE_UNDOCKED);
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/log/BiometricFrameworkStatsLoggerTest.java b/services/tests/servicestests/src/com/android/server/biometrics/log/BiometricFrameworkStatsLoggerTest.java
index 5cff48d..4119352 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/log/BiometricFrameworkStatsLoggerTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/log/BiometricFrameworkStatsLoggerTest.java
@@ -18,6 +18,7 @@
import static com.google.common.truth.Truth.assertThat;
+import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.common.AuthenticateReason;
import android.hardware.biometrics.common.OperationContext;
@@ -48,11 +49,13 @@
public void testConvertsWakeReason_whenPowerReason() {
final OperationContext context = new OperationContext();
context.wakeReason = WakeReason.WAKE_MOTION;
- final OperationContextExt ctx = new OperationContextExt(context, false);
+ final OperationContextExt ctx = new OperationContextExt(context, false,
+ BiometricAuthenticator.TYPE_NONE);
final int reason = BiometricFrameworkStatsLogger.toProtoWakeReason(ctx);
final int[] reasonDetails = BiometricFrameworkStatsLogger
- .toProtoWakeReasonDetails(new OperationContextExt(context, false));
+ .toProtoWakeReasonDetails(
+ new OperationContextExt(context, false, BiometricAuthenticator.TYPE_NONE));
assertThat(reason).isEqualTo(BiometricsProtoEnums.WAKE_REASON_WAKE_MOTION);
assertThat(reasonDetails).isEmpty();
@@ -63,7 +66,8 @@
final OperationContext context = new OperationContext();
context.authenticateReason = AuthenticateReason.faceAuthenticateReason(
AuthenticateReason.Face.ASSISTANT_VISIBLE);
- final OperationContextExt ctx = new OperationContextExt(context, false);
+ final OperationContextExt ctx = new OperationContextExt(context, false,
+ BiometricAuthenticator.TYPE_NONE);
final int reason = BiometricFrameworkStatsLogger.toProtoWakeReason(ctx);
final int[] reasonDetails = BiometricFrameworkStatsLogger
@@ -79,7 +83,8 @@
final OperationContext context = new OperationContext();
context.authenticateReason = AuthenticateReason.vendorAuthenticateReason(
new AuthenticateReason.Vendor());
- final OperationContextExt ctx = new OperationContextExt(context, false);
+ final OperationContextExt ctx = new OperationContextExt(context, false,
+ BiometricAuthenticator.TYPE_NONE);
final int reason = BiometricFrameworkStatsLogger.toProtoWakeReason(ctx);
final int[] reasonDetails = BiometricFrameworkStatsLogger
@@ -96,7 +101,8 @@
context.wakeReason = WakeReason.WAKE_KEY;
context.authenticateReason = AuthenticateReason.faceAuthenticateReason(
AuthenticateReason.Face.PRIMARY_BOUNCER_SHOWN);
- final OperationContextExt ctx = new OperationContextExt(context, false);
+ final OperationContextExt ctx = new OperationContextExt(context, false,
+ BiometricAuthenticator.TYPE_NONE);
final int reason = BiometricFrameworkStatsLogger.toProtoWakeReason(ctx);
final int[] reasonDetails = BiometricFrameworkStatsLogger
@@ -113,7 +119,8 @@
context.wakeReason = WakeReason.LID;
context.authenticateReason = AuthenticateReason.vendorAuthenticateReason(
new AuthenticateReason.Vendor());
- final OperationContextExt ctx = new OperationContextExt(context, false);
+ final OperationContextExt ctx = new OperationContextExt(context, false,
+ BiometricAuthenticator.TYPE_NONE);
final int reason = BiometricFrameworkStatsLogger.toProtoWakeReason(ctx);
final int[] reasonDetails = BiometricFrameworkStatsLogger
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/log/OperationContextExtTest.java b/services/tests/servicestests/src/com/android/server/biometrics/log/OperationContextExtTest.java
index 32284fd..767b426 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/log/OperationContextExtTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/log/OperationContextExtTest.java
@@ -18,17 +18,19 @@
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.when;
+
import android.content.Intent;
import android.hardware.biometrics.AuthenticateOptions;
+import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.IBiometricContextListener;
import android.hardware.biometrics.common.DisplayState;
import android.hardware.biometrics.common.OperationContext;
import android.hardware.biometrics.common.OperationReason;
+import android.hardware.biometrics.common.OperationState;
import android.platform.test.annotations.Presubmit;
import android.view.Surface;
-import static org.mockito.Mockito.when;
-
import androidx.test.filters.SmallTest;
import com.android.internal.logging.InstanceId;
@@ -58,7 +60,7 @@
final OperationContext aidlContext = newAidlContext();
- context = new OperationContextExt(aidlContext, false);
+ context = new OperationContextExt(aidlContext, false, BiometricAuthenticator.TYPE_NONE);
assertThat(context.toAidlContext()).isSameInstanceAs(aidlContext);
final int id = 5;
@@ -96,7 +98,8 @@
);
for (Map.Entry<Integer, Integer> entry : map.entrySet()) {
- final OperationContextExt context = new OperationContextExt(newAidlContext(), true);
+ final OperationContextExt context = new OperationContextExt(newAidlContext(), true,
+ BiometricAuthenticator.TYPE_NONE);
when(mBiometricContext.getDisplayState()).thenReturn(entry.getKey());
assertThat(context.update(mBiometricContext, context.isCrypto()).getDisplayState())
.isEqualTo(entry.getValue());
@@ -124,7 +127,7 @@
updatesFromSource(null, OperationReason.UNKNOWN);
}
- private void updatesFromSource(BiometricContextSessionInfo sessionInfo, int sessionType) {
+ private void updatesFromSource(BiometricContextSessionInfo sessionInfo, int sessionType) {
final int rotation = Surface.ROTATION_270;
final int foldState = IBiometricContextListener.FoldState.HALF_OPENED;
final int dockState = Intent.EXTRA_DOCK_STATE_CAR;
@@ -135,9 +138,11 @@
when(mBiometricContext.getDockedState()).thenReturn(dockState);
when(mBiometricContext.isDisplayOn()).thenReturn(true);
when(mBiometricContext.getDisplayState()).thenReturn(displayState);
+ when(mBiometricContext.isHardwareIgnoringTouches()).thenReturn(true);
final OperationContextExt context = new OperationContextExt(newAidlContext(),
- sessionType == OperationReason.BIOMETRIC_PROMPT);
+ sessionType == OperationReason.BIOMETRIC_PROMPT,
+ BiometricAuthenticator.TYPE_FINGERPRINT);
assertThat(context.update(mBiometricContext, context.isCrypto())).isSameInstanceAs(context);
@@ -154,6 +159,46 @@
assertThat(context.getOrientation()).isEqualTo(rotation);
assertThat(context.isDisplayOn()).isTrue();
assertThat(context.getDisplayState()).isEqualTo(DisplayState.AOD);
+ assertThat(
+ context.getOperationState().getFingerprintOperationState().isHardwareIgnoringTouches
+ ).isTrue();
+ }
+
+ @Test
+ public void hasNullOperationState() {
+ OperationContextExt context = new OperationContextExt(false);
+ assertThat(context.toAidlContext()).isNotNull();
+
+ final OperationContext aidlContext = newAidlContext();
+
+ context = new OperationContextExt(aidlContext, false, BiometricAuthenticator.TYPE_NONE);
+ assertThat(context.getOperationState()).isNull();
+ }
+
+ @Test
+ public void hasFaceOperationState() {
+ OperationContextExt context = new OperationContextExt(false);
+ assertThat(context.toAidlContext()).isNotNull();
+
+ final OperationContext aidlContext = newAidlContext();
+
+ context = new OperationContextExt(aidlContext, false,
+ BiometricAuthenticator.TYPE_FACE);
+ assertThat(context.getOperationState().getTag()).isEqualTo(
+ OperationState.faceOperationState);
+ }
+
+ @Test
+ public void hasFingerprintOperationState() {
+ OperationContextExt context = new OperationContextExt(false);
+ assertThat(context.toAidlContext()).isNotNull();
+
+ final OperationContext aidlContext = newAidlContext();
+
+ context = new OperationContextExt(aidlContext, false,
+ BiometricAuthenticator.TYPE_FINGERPRINT);
+ assertThat(context.getOperationState().getTag()).isEqualTo(
+ OperationState.fingerprintOperationState);
}
private static OperationContext newAidlContext() {
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/AcquisitionClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/AcquisitionClientTest.java
index 2d9d868..4604b31 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/AcquisitionClientTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/AcquisitionClientTest.java
@@ -72,6 +72,7 @@
mToken, mClientCallback);
client.start(mSchedulerCallback);
assertTrue(client.mHalOperationRunning);
+ verify(mClientCallback).getModality();
verify(mSchedulerCallback).onClientStarted(eq(client));
// Pretend that it got canceled by the user.
diff --git a/services/tests/servicestests/src/com/android/server/inputmethod/InputMethodSettingsTest.java b/services/tests/servicestests/src/com/android/server/inputmethod/InputMethodSettingsTest.java
new file mode 100644
index 0000000..a55d1c4
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/inputmethod/InputMethodSettingsTest.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.inputmethod;
+
+import static org.junit.Assert.assertEquals;
+
+import android.text.TextUtils;
+import android.util.IntArray;
+
+import androidx.annotation.NonNull;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public final class InputMethodSettingsTest {
+ private static void verifyUpdateEnabledImeString(@NonNull String expectedEnabledImeStr,
+ @NonNull String initialEnabledImeStr, @NonNull String imeId,
+ @NonNull String enabledSubtypeHashCodesStr) {
+ assertEquals(expectedEnabledImeStr,
+ InputMethodSettings.updateEnabledImeString(initialEnabledImeStr,
+ imeId, createSubtypeHashCodeArrayFromStr(enabledSubtypeHashCodesStr)));
+ }
+
+ private static IntArray createSubtypeHashCodeArrayFromStr(String subtypeHashCodesStr) {
+ final IntArray subtypes = new IntArray();
+ final TextUtils.SimpleStringSplitter imeSubtypeSplitter =
+ new TextUtils.SimpleStringSplitter(';');
+ if (TextUtils.isEmpty(subtypeHashCodesStr)) {
+ return subtypes;
+ }
+ imeSubtypeSplitter.setString(subtypeHashCodesStr);
+ while (imeSubtypeSplitter.hasNext()) {
+ subtypes.add(Integer.parseInt(imeSubtypeSplitter.next()));
+ }
+ return subtypes;
+ }
+
+ @Test
+ public void updateEnabledImeStringTest() {
+ // No change cases
+ verifyUpdateEnabledImeString(
+ "com.android/.ime1",
+ "com.android/.ime1", "com.android/.ime1", "");
+ verifyUpdateEnabledImeString(
+ "com.android/.ime1",
+ "com.android/.ime1", "com.android/.ime2", "");
+
+ // To enable subtypes
+ verifyUpdateEnabledImeString(
+ "com.android/.ime1",
+ "com.android/.ime1", "com.android/.ime2", "");
+ verifyUpdateEnabledImeString(
+ "com.android/.ime1;1",
+ "com.android/.ime1", "com.android/.ime1", "1");
+
+ verifyUpdateEnabledImeString(
+ "com.android/.ime1;1;2;3",
+ "com.android/.ime1", "com.android/.ime1", "1;2;3");
+
+ verifyUpdateEnabledImeString(
+ "com.android/.ime1;1;2;3:com.android/.ime2",
+ "com.android/.ime1:com.android/.ime2", "com.android/.ime1", "1;2;3");
+ verifyUpdateEnabledImeString(
+ "com.android/.ime0:com.android/.ime1;1;2;3",
+ "com.android/.ime0:com.android/.ime1", "com.android/.ime1", "1;2;3");
+ verifyUpdateEnabledImeString(
+ "com.android/.ime0:com.android/.ime1;1;2;3:com.android/.ime2",
+ "com.android/.ime0:com.android/.ime1:com.android/.ime2", "com.android/.ime1",
+ "1;2;3");
+
+ // To reset enabled subtypes
+ verifyUpdateEnabledImeString(
+ "com.android/.ime1",
+ "com.android/.ime1;1", "com.android/.ime1", "");
+ verifyUpdateEnabledImeString(
+ "com.android/.ime1",
+ "com.android/.ime1;1;2;3", "com.android/.ime1", "");
+ verifyUpdateEnabledImeString(
+ "com.android/.ime1:com.android/.ime2",
+ "com.android/.ime1;1;2;3:com.android/.ime2", "com.android/.ime1", "");
+
+ verifyUpdateEnabledImeString(
+ "com.android/.ime0:com.android/.ime1",
+ "com.android/.ime0:com.android/.ime1;1;2;3", "com.android/.ime1", "");
+ verifyUpdateEnabledImeString(
+ "com.android/.ime0:com.android/.ime1:com.android/.ime2",
+ "com.android/.ime0:com.android/.ime1;1;2;3:com.android/.ime2", "com.android/.ime1",
+ "");
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/inputmethod/InputMethodUtilsTest.java b/services/tests/servicestests/src/com/android/server/inputmethod/InputMethodUtilsTest.java
index 95a9610..9688ef6 100644
--- a/services/tests/servicestests/src/com/android/server/inputmethod/InputMethodUtilsTest.java
+++ b/services/tests/servicestests/src/com/android/server/inputmethod/InputMethodUtilsTest.java
@@ -34,9 +34,7 @@
import android.os.Build;
import android.os.LocaleList;
import android.os.Parcel;
-import android.text.TextUtils;
import android.util.ArrayMap;
-import android.util.IntArray;
import android.view.inputmethod.InputMethodInfo;
import android.view.inputmethod.InputMethodSubtype;
import android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder;
@@ -1211,28 +1209,6 @@
StartInputFlags.VIEW_HAS_FOCUS | StartInputFlags.IS_TEXT_EDITOR));
}
- private static IntArray createSubtypeHashCodeArrayFromStr(String subtypeHashCodesStr) {
- final IntArray subtypes = new IntArray();
- final TextUtils.SimpleStringSplitter imeSubtypeSplitter =
- new TextUtils.SimpleStringSplitter(';');
- if (TextUtils.isEmpty(subtypeHashCodesStr)) {
- return subtypes;
- }
- imeSubtypeSplitter.setString(subtypeHashCodesStr);
- while (imeSubtypeSplitter.hasNext()) {
- subtypes.add(Integer.parseInt(imeSubtypeSplitter.next()));
- }
- return subtypes;
- }
-
- private static void verifyUpdateEnabledImeString(@NonNull String expectedEnabledImeStr,
- @NonNull String initialEnabledImeStr, @NonNull String imeId,
- @NonNull String enabledSubtypeHashCodesStr) {
- assertEquals(expectedEnabledImeStr,
- InputMethodUtils.InputMethodSettings.updateEnabledImeString(initialEnabledImeStr,
- imeId, createSubtypeHashCodeArrayFromStr(enabledSubtypeHashCodesStr)));
- }
-
private static void verifySplitEnabledImeStr(@NonNull String enabledImeStr,
@NonNull String... expected) {
final ArrayList<String> actual = new ArrayList<>();
@@ -1280,57 +1256,4 @@
"com.android/.ime1:com.android/.ime2", "com.android/.ime3"))
.isEqualTo("com.android/.ime1:com.android/.ime2:com.android/.ime3");
}
-
- @Test
- public void updateEnabledImeStringTest() {
- // No change cases
- verifyUpdateEnabledImeString(
- "com.android/.ime1",
- "com.android/.ime1", "com.android/.ime1", "");
- verifyUpdateEnabledImeString(
- "com.android/.ime1",
- "com.android/.ime1", "com.android/.ime2", "");
-
- // To enable subtypes
- verifyUpdateEnabledImeString(
- "com.android/.ime1",
- "com.android/.ime1", "com.android/.ime2", "");
- verifyUpdateEnabledImeString(
- "com.android/.ime1;1",
- "com.android/.ime1", "com.android/.ime1", "1");
-
- verifyUpdateEnabledImeString(
- "com.android/.ime1;1;2;3",
- "com.android/.ime1", "com.android/.ime1", "1;2;3");
-
- verifyUpdateEnabledImeString(
- "com.android/.ime1;1;2;3:com.android/.ime2",
- "com.android/.ime1:com.android/.ime2", "com.android/.ime1", "1;2;3");
- verifyUpdateEnabledImeString(
- "com.android/.ime0:com.android/.ime1;1;2;3",
- "com.android/.ime0:com.android/.ime1", "com.android/.ime1", "1;2;3");
- verifyUpdateEnabledImeString(
- "com.android/.ime0:com.android/.ime1;1;2;3:com.android/.ime2",
- "com.android/.ime0:com.android/.ime1:com.android/.ime2", "com.android/.ime1",
- "1;2;3");
-
- // To reset enabled subtypes
- verifyUpdateEnabledImeString(
- "com.android/.ime1",
- "com.android/.ime1;1", "com.android/.ime1", "");
- verifyUpdateEnabledImeString(
- "com.android/.ime1",
- "com.android/.ime1;1;2;3", "com.android/.ime1", "");
- verifyUpdateEnabledImeString(
- "com.android/.ime1:com.android/.ime2",
- "com.android/.ime1;1;2;3:com.android/.ime2", "com.android/.ime1", "");
-
- verifyUpdateEnabledImeString(
- "com.android/.ime0:com.android/.ime1",
- "com.android/.ime0:com.android/.ime1;1;2;3", "com.android/.ime1", "");
- verifyUpdateEnabledImeString(
- "com.android/.ime0:com.android/.ime1:com.android/.ime2",
- "com.android/.ime0:com.android/.ime1;1;2;3:com.android/.ime2", "com.android/.ime1",
- "");
- }
}