summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--apct-tests/perftests/core/Android.bp1
-rw-r--r--apct-tests/perftests/core/src/android/os/TracePerfTest.java75
-rw-r--r--core/java/android/hardware/input/InputSettings.java7
-rw-r--r--core/java/android/os/PerfettoTrace.java36
-rw-r--r--core/java/android/os/PowerManager.java8
-rw-r--r--core/java/android/os/Trace.java4
-rw-r--r--core/jni/android_os_PerfettoTrace.cpp42
-rw-r--r--core/jni/android_os_Trace.cpp5
-rw-r--r--core/tests/coretests/Android.bp1
-rw-r--r--core/tests/coretests/jni/Android.bp24
-rw-r--r--core/tests/coretests/jni/PerfettoTraceTest.cpp117
-rw-r--r--core/tests/coretests/src/android/os/PerfettoTraceTest.java61
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt4
-rw-r--r--media/java/android/media/OWNERS3
-rw-r--r--packages/SystemUI/aconfig/systemui.aconfig12
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContent.kt17
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/section/CommunalToDreamButtonSection.kt141
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalPrefsRepositoryImplTest.kt28
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalSettingsRepositoryImplTest.kt222
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt500
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalPrefsInteractorTest.kt37
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalSettingsInteractorTest.kt40
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/viewmodel/CommunalToDreamButtonViewModelTest.kt39
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryHapticsInteractorTest.kt4
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt16
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.kt76
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationSnoozeTest.java112
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/DisplaySwitchLatencyTrackerTest.kt139
-rw-r--r--packages/SystemUI/res/values/strings.xml2
-rw-r--r--packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/authentication/shared/model/AuthenticationMethodModel.kt11
-rw-r--r--packages/SystemUI/src/com/android/systemui/common/data/CommonDataLayerModule.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/common/data/repository/BatteryRepository.kt44
-rw-r--r--packages/SystemUI/src/com/android/systemui/common/domain/interactor/BatteryInteractor.kt26
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/DevicePosturingListener.kt1
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalMediaRepository.kt1
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalPrefsRepository.kt36
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSettingsRepository.kt50
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalTutorialRepository.kt1
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt39
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalPrefsInteractor.kt20
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSettingsInteractor.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractor.kt1
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/shared/model/WhenToDream.kt24
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalToDreamButtonViewModel.kt31
-rw-r--r--packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryBiometricAuthInteractor.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractor.kt15
-rw-r--r--packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryHapticsInteractor.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt42
-rw-r--r--packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceUnlockedInteractor.kt43
-rw-r--r--packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/NoopDeviceEntryFaceAuthInteractor.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/SystemUIDeviceEntryFaceAuthInteractor.kt26
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt35
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardEnabledInteractor.kt13
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt13
-rw-r--r--packages/SystemUI/src/com/android/systemui/power/domain/interactor/PowerInteractor.kt12
-rw-r--r--packages/SystemUI/src/com/android/systemui/power/shared/model/WakefulnessModel.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/scene/domain/SceneDomainModule.kt28
-rw-r--r--packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneBackInteractor.kt27
-rw-r--r--packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt44
-rw-r--r--packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt14
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeModeInteractor.kt24
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/shared/model/ShadeMode.kt15
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/binder/OngoingActivityChipBinder.kt1
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/ChipContent.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/OngoingActivityChipModel.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinator.kt20
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGuts.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java22
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationSnooze.java38
-rw-r--r--packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/TutorialSelectionScreen.kt130
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorKosmos.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/common/data/repository/BatteryRepositoryKosmos.kt21
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/common/data/repository/FakeBatteryRepository.kt34
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/common/domain/interactor/BatteryInteractorKosmos.kt22
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalPrefsRepository.kt17
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorKosmos.kt6
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/communal/ui/viewmodel/CommunalToDreamButtonViewModelKosmos.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorKosmos.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceUnlockedInteractorKosmos.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorKosmos.kt4
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/interactor/SceneBackInteractorKosmos.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/startable/SceneContainerStartableKosmos.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeModeInteractorKosmos.kt2
-rw-r--r--services/core/java/com/android/server/DockObserver.java68
-rw-r--r--services/core/java/com/android/server/am/CachedAppOptimizer.java22
-rw-r--r--services/core/java/com/android/server/audio/AudioService.java104
-rw-r--r--services/core/java/com/android/server/wm/AppCompatAspectRatioPolicy.java7
-rw-r--r--services/core/jni/com_android_server_input_InputManagerService.cpp18
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java24
-rw-r--r--tests/CompanionDeviceMultiDeviceTests/host/Android.bp9
-rw-r--r--tests/Input/src/android/hardware/input/StickyModifierStateListenerTest.kt5
92 files changed, 2025 insertions, 1018 deletions
diff --git a/apct-tests/perftests/core/Android.bp b/apct-tests/perftests/core/Android.bp
index 1e299cdf8002..f16f2caccd49 100644
--- a/apct-tests/perftests/core/Android.bp
+++ b/apct-tests/perftests/core/Android.bp
@@ -50,6 +50,7 @@ android_test {
"junit-params",
"core-tests-support",
"guava",
+ "perfetto_trace_java_protos",
],
libs: ["android.test.base.stubs.system"],
diff --git a/apct-tests/perftests/core/src/android/os/TracePerfTest.java b/apct-tests/perftests/core/src/android/os/TracePerfTest.java
index 0d64c390f4c2..bf7c96a3cb85 100644
--- a/apct-tests/perftests/core/src/android/os/TracePerfTest.java
+++ b/apct-tests/perftests/core/src/android/os/TracePerfTest.java
@@ -17,6 +17,8 @@
package android.os;
+import static android.os.PerfettoTrace.Category;
+
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
import android.perftests.utils.ShellHelper;
@@ -31,19 +33,35 @@ import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
+import perfetto.protos.DataSourceConfigOuterClass.DataSourceConfig;
+import perfetto.protos.TraceConfigOuterClass.TraceConfig;
+import perfetto.protos.TraceConfigOuterClass.TraceConfig.BufferConfig;
+import perfetto.protos.TraceConfigOuterClass.TraceConfig.DataSource;
+import perfetto.protos.TrackEventConfigOuterClass.TrackEventConfig;
+
@RunWith(AndroidJUnit4.class)
public class TracePerfTest {
@Rule
public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+ private static final String FOO = "foo";
+ private static final Category FOO_CATEGORY = new Category(FOO);
+ private static PerfettoTrace.Session sPerfettoSession;
+
@BeforeClass
public static void startTracing() {
ShellHelper.runShellCommandRaw("atrace -c --async_start -a *");
+ PerfettoTrace.register(false /* isBackendInProcess */);
+ FOO_CATEGORY.register();
+ sPerfettoSession = new PerfettoTrace.Session(false /* isBackendInProcess */,
+ getTraceConfig(FOO).toByteArray());
}
@AfterClass
public static void endTracing() {
ShellHelper.runShellCommandRaw("atrace --async_stop");
+ FOO_CATEGORY.unregister();
+ sPerfettoSession.close();
}
@Before
@@ -84,4 +102,61 @@ public class TracePerfTest {
Trace.setCounter("testCounter", 123);
}
}
+
+ @Test
+ public void testInstant() {
+ Trace.instant(Trace.TRACE_TAG_APP, "testInstantA");
+
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ Trace.instant(Trace.TRACE_TAG_APP, "testInstantA");
+ }
+ }
+
+ @Test
+ public void testInstantPerfetto() {
+ PerfettoTrace.instant(FOO_CATEGORY, "testInstantP").emit();
+
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ PerfettoTrace.instant(FOO_CATEGORY, "testInstantP").emit();
+ }
+ }
+
+ @Test
+ public void testInstantPerfettoWithArgs() {
+ PerfettoTrace.instant(FOO_CATEGORY, "testInstantP")
+ .addArg("foo", "bar")
+ .addFlow(1)
+ .emit();
+
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ PerfettoTrace.instant(FOO_CATEGORY, "testInstantP")
+ .addArg("foo", "bar")
+ .addFlow(1)
+ .emit();
+ }
+ }
+
+ private static TraceConfig getTraceConfig(String cat) {
+ BufferConfig bufferConfig = BufferConfig.newBuilder().setSizeKb(1024).build();
+ TrackEventConfig trackEventConfig = TrackEventConfig
+ .newBuilder()
+ .addEnabledCategories(cat)
+ .build();
+ DataSourceConfig dsConfig = DataSourceConfig
+ .newBuilder()
+ .setName("track_event")
+ .setTargetBuffer(0)
+ .setTrackEventConfig(trackEventConfig)
+ .build();
+ DataSource ds = DataSource.newBuilder().setConfig(dsConfig).build();
+ TraceConfig traceConfig = TraceConfig
+ .newBuilder()
+ .addBuffers(bufferConfig)
+ .addDataSources(ds)
+ .build();
+ return traceConfig;
+ }
}
diff --git a/core/java/android/hardware/input/InputSettings.java b/core/java/android/hardware/input/InputSettings.java
index cd48047bccb4..af40188c4eba 100644
--- a/core/java/android/hardware/input/InputSettings.java
+++ b/core/java/android/hardware/input/InputSettings.java
@@ -35,7 +35,6 @@ import static com.android.hardware.input.Flags.touchpadVisualizer;
import static com.android.hardware.input.Flags.useKeyGestureEventHandler;
import static com.android.hardware.input.Flags.useKeyGestureEventHandlerMultiKeyGestures;
import static com.android.input.flags.Flags.FLAG_KEYBOARD_REPEAT_KEYS;
-import static com.android.input.flags.Flags.enableInputFilterRustImpl;
import static com.android.input.flags.Flags.keyboardRepeatKeys;
import android.Manifest;
@@ -883,7 +882,7 @@ public class InputSettings {
* @hide
*/
public static boolean isAccessibilityBounceKeysFeatureEnabled() {
- return keyboardA11yBounceKeysFlag() && enableInputFilterRustImpl();
+ return keyboardA11yBounceKeysFlag();
}
/**
@@ -967,7 +966,7 @@ public class InputSettings {
* @hide
*/
public static boolean isAccessibilitySlowKeysFeatureFlagEnabled() {
- return keyboardA11ySlowKeysFlag() && enableInputFilterRustImpl();
+ return keyboardA11ySlowKeysFlag();
}
/**
@@ -1053,7 +1052,7 @@ public class InputSettings {
* @hide
*/
public static boolean isAccessibilityStickyKeysFeatureEnabled() {
- return keyboardA11yStickyKeysFlag() && enableInputFilterRustImpl();
+ return keyboardA11yStickyKeysFlag();
}
/**
diff --git a/core/java/android/os/PerfettoTrace.java b/core/java/android/os/PerfettoTrace.java
index e3f251e34b45..68f1570154ff 100644
--- a/core/java/android/os/PerfettoTrace.java
+++ b/core/java/android/os/PerfettoTrace.java
@@ -154,14 +154,44 @@ public final class PerfettoTrace {
}
}
+ /**
+ * Manages a perfetto tracing session.
+ * Constructing this object with a config automatically starts a tracing session. Each session
+ * must be closed after use and then the resulting trace bytes can be read.
+ *
+ * The session could be in process or system wide, depending on {@code isBackendInProcess}.
+ * This functionality is intended for testing.
+ */
+ public static final class Session {
+ private final long mPtr;
+
+ /**
+ * Session ctor.
+ */
+ public Session(boolean isBackendInProcess, byte[] config) {
+ mPtr = native_start_session(isBackendInProcess, config);
+ }
+
+ /**
+ * Closes the session and returns the trace.
+ */
+ public byte[] close() {
+ return native_stop_session(mPtr);
+ }
+ }
+
@CriticalNative
private static native long native_get_process_track_uuid();
-
@CriticalNative
private static native long native_get_thread_track_uuid(long tid);
@FastNative
private static native void native_activate_trigger(String name, int ttlMs);
+ @FastNative
+ private static native void native_register(boolean isBackendInProcess);
+
+ private static native long native_start_session(boolean isBackendInProcess, byte[] config);
+ private static native byte[] native_stop_session(long ptr);
/**
* Writes a trace message to indicate a given section of code was invoked.
@@ -307,7 +337,7 @@ public final class PerfettoTrace {
/**
* Registers the process with Perfetto.
*/
- public static void register() {
- Trace.registerWithPerfetto();
+ public static void register(boolean isBackendInProcess) {
+ native_register(isBackendInProcess);
}
}
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index 2a5666cbe83c..e769abec7dd9 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -624,6 +624,7 @@ public final class PowerManager {
WAKE_REASON_TAP,
WAKE_REASON_LIFT,
WAKE_REASON_BIOMETRIC,
+ WAKE_REASON_DOCK,
})
@Retention(RetentionPolicy.SOURCE)
public @interface WakeReason{}
@@ -765,6 +766,12 @@ public final class PowerManager {
public static final int WAKE_REASON_BIOMETRIC = 17;
/**
+ * Wake up reason code: Waking up due to a user docking the device.
+ * @hide
+ */
+ public static final int WAKE_REASON_DOCK = 18;
+
+ /**
* Convert the wake reason to a string for debugging purposes.
* @hide
*/
@@ -788,6 +795,7 @@ public final class PowerManager {
case WAKE_REASON_TAP: return "WAKE_REASON_TAP";
case WAKE_REASON_LIFT: return "WAKE_REASON_LIFT";
case WAKE_REASON_BIOMETRIC: return "WAKE_REASON_BIOMETRIC";
+ case WAKE_REASON_DOCK: return "WAKE_REASON_DOCK";
default: return Integer.toString(wakeReason);
}
}
diff --git a/core/java/android/os/Trace.java b/core/java/android/os/Trace.java
index 4a37e0a70443..09e6a45dc294 100644
--- a/core/java/android/os/Trace.java
+++ b/core/java/android/os/Trace.java
@@ -164,8 +164,6 @@ public final class Trace {
private static native void nativeInstant(long tag, String name);
@FastNative
private static native void nativeInstantForTrack(long tag, String trackName, String name);
- @FastNative
- private static native void nativeRegisterWithPerfetto();
private Trace() {
}
@@ -545,6 +543,6 @@ public final class Trace {
* @hide
*/
public static void registerWithPerfetto() {
- nativeRegisterWithPerfetto();
+ PerfettoTrace.register(false /* isBackendInProcess */);
}
}
diff --git a/core/jni/android_os_PerfettoTrace.cpp b/core/jni/android_os_PerfettoTrace.cpp
index 962aefc482e4..9bedfa27fa1a 100644
--- a/core/jni/android_os_PerfettoTrace.cpp
+++ b/core/jni/android_os_PerfettoTrace.cpp
@@ -24,9 +24,12 @@
#include <nativehelper/scoped_primitive_array.h>
#include <nativehelper/scoped_utf_chars.h>
#include <nativehelper/utils.h>
+#include <tracing_perfetto.h>
#include <tracing_sdk.h>
namespace android {
+constexpr int kFlushTimeoutMs = 5000;
+
template <typename T>
inline static T* toPointer(jlong ptr) {
return reinterpret_cast<T*>(static_cast<uintptr_t>(ptr));
@@ -51,6 +54,10 @@ static void android_os_PerfettoTrace_activate_trigger(JNIEnv* env, jclass, jstri
tracing_perfetto::activate_trigger(name_chars.c_str(), static_cast<uint32_t>(ttl_ms));
}
+void android_os_PerfettoTrace_register(bool is_backend_in_process) {
+ tracing_perfetto::registerWithPerfetto(is_backend_in_process);
+}
+
static jlong android_os_PerfettoTraceCategory_init(JNIEnv* env, jclass, jstring name, jstring tag,
jstring severity) {
ScopedUtfChars name_chars = GET_UTF_OR_RETURN(env, name);
@@ -85,6 +92,36 @@ static jlong android_os_PerfettoTraceCategory_get_extra_ptr(jlong ptr) {
return toJLong(category->get());
}
+static jlong android_os_PerfettoTrace_start_session(JNIEnv* env, jclass /* obj */,
+ jboolean is_backend_in_process,
+ jbyteArray config_bytes) {
+ jsize length = env->GetArrayLength(config_bytes);
+ std::vector<uint8_t> data;
+ data.reserve(length);
+ env->GetByteArrayRegion(config_bytes, 0, length, reinterpret_cast<jbyte*>(data.data()));
+
+ tracing_perfetto::Session* session =
+ new tracing_perfetto::Session(is_backend_in_process, data.data(), length);
+
+ return reinterpret_cast<long>(session);
+}
+
+static jbyteArray android_os_PerfettoTrace_stop_session([[maybe_unused]] JNIEnv* env,
+ jclass /* obj */, jlong ptr) {
+ tracing_perfetto::Session* session = reinterpret_cast<tracing_perfetto::Session*>(ptr);
+
+ session->FlushBlocking(kFlushTimeoutMs);
+ session->StopBlocking();
+
+ std::vector<uint8_t> data = session->ReadBlocking();
+
+ delete session;
+
+ jbyteArray bytes = env->NewByteArray(data.size());
+ env->SetByteArrayRegion(bytes, 0, data.size(), reinterpret_cast<jbyte*>(data.data()));
+ return bytes;
+}
+
static const JNINativeMethod gCategoryMethods[] = {
{"native_init", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)J",
(void*)android_os_PerfettoTraceCategory_init},
@@ -101,7 +138,10 @@ static const JNINativeMethod gTraceMethods[] =
{"native_get_thread_track_uuid", "(J)J",
(void*)android_os_PerfettoTrace_get_thread_track_uuid},
{"native_activate_trigger", "(Ljava/lang/String;I)V",
- (void*)android_os_PerfettoTrace_activate_trigger}};
+ (void*)android_os_PerfettoTrace_activate_trigger},
+ {"native_register", "(Z)V", (void*)android_os_PerfettoTrace_register},
+ {"native_start_session", "(Z[B)J", (void*)android_os_PerfettoTrace_start_session},
+ {"native_stop_session", "(J)[B", (void*)android_os_PerfettoTrace_stop_session}};
int register_android_os_PerfettoTrace(JNIEnv* env) {
int res = jniRegisterNativeMethods(env, "android/os/PerfettoTrace", gTraceMethods,
diff --git a/core/jni/android_os_Trace.cpp b/core/jni/android_os_Trace.cpp
index 21e056dfa12b..50618c5a07af 100644
--- a/core/jni/android_os_Trace.cpp
+++ b/core/jni/android_os_Trace.cpp
@@ -131,10 +131,6 @@ static jboolean android_os_Trace_nativeIsTagEnabled(jlong tag) {
return tracing_perfetto::isTagEnabled(tag);
}
-static void android_os_Trace_nativeRegisterWithPerfetto(JNIEnv* env) {
- tracing_perfetto::registerWithPerfetto();
-}
-
static const JNINativeMethod gTraceMethods[] = {
/* name, signature, funcPtr */
{"nativeSetAppTracingAllowed", "(Z)V", (void*)android_os_Trace_nativeSetAppTracingAllowed},
@@ -157,7 +153,6 @@ static const JNINativeMethod gTraceMethods[] = {
{"nativeInstant", "(JLjava/lang/String;)V", (void*)android_os_Trace_nativeInstant},
{"nativeInstantForTrack", "(JLjava/lang/String;Ljava/lang/String;)V",
(void*)android_os_Trace_nativeInstantForTrack},
- {"nativeRegisterWithPerfetto", "()V", (void*)android_os_Trace_nativeRegisterWithPerfetto},
// ----------- @CriticalNative ----------------
{"nativeIsTagEnabled", "(J)Z", (void*)android_os_Trace_nativeIsTagEnabled},
diff --git a/core/tests/coretests/Android.bp b/core/tests/coretests/Android.bp
index 1b6746ca4b63..c06ad64cc0f5 100644
--- a/core/tests/coretests/Android.bp
+++ b/core/tests/coretests/Android.bp
@@ -122,7 +122,6 @@ android_test {
"android.view.flags-aconfig-java",
],
jni_libs: [
- "libperfetto_trace_test_jni",
"libpowermanagertest_jni",
"libviewRootImplTest_jni",
"libworksourceparceltest_jni",
diff --git a/core/tests/coretests/jni/Android.bp b/core/tests/coretests/jni/Android.bp
index 798ec90eb884..d6379ca8c3e6 100644
--- a/core/tests/coretests/jni/Android.bp
+++ b/core/tests/coretests/jni/Android.bp
@@ -111,27 +111,3 @@ cc_test_library {
],
gtest: false,
}
-
-cc_test_library {
- name: "libperfetto_trace_test_jni",
- srcs: [
- "PerfettoTraceTest.cpp",
- ],
- static_libs: [
- "perfetto_trace_protos",
- "libtracing_perfetto_test_utils",
- ],
- shared_libs: [
- "liblog",
- "libnativehelper",
- "libperfetto_c",
- "libprotobuf-cpp-lite",
- "libtracing_perfetto",
- ],
- stl: "libc++_static",
- cflags: [
- "-Werror",
- "-Wall",
- ],
- gtest: false,
-}
diff --git a/core/tests/coretests/jni/PerfettoTraceTest.cpp b/core/tests/coretests/jni/PerfettoTraceTest.cpp
deleted file mode 100644
index 41d02ed70c9a..000000000000
--- a/core/tests/coretests/jni/PerfettoTraceTest.cpp
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-// #define LOG_NDEBUG 0
-#define LOG_TAG "PerfettoTraceTest"
-
-#include <nativehelper/JNIHelp.h>
-#include <utils/Log.h>
-
-#include "jni.h"
-#include "perfetto/public/abi/data_source_abi.h"
-#include "perfetto/public/abi/heap_buffer.h"
-#include "perfetto/public/abi/pb_decoder_abi.h"
-#include "perfetto/public/abi/tracing_session_abi.h"
-#include "perfetto/public/abi/track_event_abi.h"
-#include "perfetto/public/compiler.h"
-#include "perfetto/public/data_source.h"
-#include "perfetto/public/pb_decoder.h"
-#include "perfetto/public/producer.h"
-#include "perfetto/public/protos/config/trace_config.pzc.h"
-#include "perfetto/public/protos/trace/interned_data/interned_data.pzc.h"
-#include "perfetto/public/protos/trace/test_event.pzc.h"
-#include "perfetto/public/protos/trace/trace.pzc.h"
-#include "perfetto/public/protos/trace/trace_packet.pzc.h"
-#include "perfetto/public/protos/trace/track_event/debug_annotation.pzc.h"
-#include "perfetto/public/protos/trace/track_event/track_descriptor.pzc.h"
-#include "perfetto/public/protos/trace/track_event/track_event.pzc.h"
-#include "perfetto/public/protos/trace/trigger.pzc.h"
-#include "perfetto/public/te_category_macros.h"
-#include "perfetto/public/te_macros.h"
-#include "perfetto/public/track_event.h"
-#include "protos/perfetto/trace/interned_data/interned_data.pb.h"
-#include "protos/perfetto/trace/trace.pb.h"
-#include "protos/perfetto/trace/trace_packet.pb.h"
-#include "tracing_perfetto.h"
-#include "utils.h"
-
-namespace android {
-using ::perfetto::protos::EventCategory;
-using ::perfetto::protos::EventName;
-using ::perfetto::protos::FtraceEvent;
-using ::perfetto::protos::FtraceEventBundle;
-using ::perfetto::protos::InternedData;
-using ::perfetto::protos::Trace;
-using ::perfetto::protos::TracePacket;
-
-using ::perfetto::shlib::test_utils::TracingSession;
-
-struct TracingSessionHolder {
- TracingSession tracing_session;
-};
-
-static void nativeRegisterPerfetto([[maybe_unused]] JNIEnv* env, jclass /* obj */) {
- tracing_perfetto::registerWithPerfetto(false /* test */);
-}
-
-static jlong nativeStartTracing(JNIEnv* env, jclass /* obj */, jbyteArray configBytes) {
- jsize length = env->GetArrayLength(configBytes);
- std::vector<uint8_t> data;
- data.reserve(length);
- env->GetByteArrayRegion(configBytes, 0, length, reinterpret_cast<jbyte*>(data.data()));
-
- TracingSession session = TracingSession::FromBytes(data.data(), length);
- TracingSessionHolder* holder = new TracingSessionHolder(std::move(session));
-
- return reinterpret_cast<long>(holder);
-}
-
-static jbyteArray nativeStopTracing([[maybe_unused]] JNIEnv* env, jclass /* obj */, jlong ptr) {
- TracingSessionHolder* holder = reinterpret_cast<TracingSessionHolder*>(ptr);
-
- // Stop
- holder->tracing_session.FlushBlocking(5000);
- holder->tracing_session.StopBlocking();
-
- std::vector<uint8_t> data = holder->tracing_session.ReadBlocking();
-
- delete holder;
-
- jbyteArray bytes = env->NewByteArray(data.size());
- env->SetByteArrayRegion(bytes, 0, data.size(), reinterpret_cast<jbyte*>(data.data()));
- return bytes;
-}
-
-extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */) {
- JNIEnv* env;
- const JNINativeMethod methodTable[] = {/* name, signature, funcPtr */
- {"nativeStartTracing", "([B)J",
- (void*)nativeStartTracing},
- {"nativeStopTracing", "(J)[B", (void*)nativeStopTracing},
- {"nativeRegisterPerfetto", "()V",
- (void*)nativeRegisterPerfetto}};
-
- if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
- return JNI_ERR;
- }
-
- jniRegisterNativeMethods(env, "android/os/PerfettoTraceTest", methodTable,
- sizeof(methodTable) / sizeof(JNINativeMethod));
-
- return JNI_VERSION_1_6;
-}
-
-} /* namespace android */
diff --git a/core/tests/coretests/src/android/os/PerfettoTraceTest.java b/core/tests/coretests/src/android/os/PerfettoTraceTest.java
index ad28383689af..0b5a44665d2b 100644
--- a/core/tests/coretests/src/android/os/PerfettoTraceTest.java
+++ b/core/tests/coretests/src/android/os/PerfettoTraceTest.java
@@ -28,7 +28,6 @@ import android.platform.test.annotations.RequiresFlagsEnabled;
import android.platform.test.flag.junit.CheckFlagsRule;
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.util.ArraySet;
-import android.util.Log;
import androidx.test.InstrumentationRegistry;
import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -84,19 +83,9 @@ public class PerfettoTraceTest {
private final Set<String> mDebugAnnotationNames = new ArraySet<>();
private final Set<String> mTrackNames = new ArraySet<>();
- static {
- try {
- System.loadLibrary("perfetto_trace_test_jni");
- Log.i(TAG, "Successfully loaded trace_test native library");
- } catch (UnsatisfiedLinkError ule) {
- Log.w(TAG, "Could not load trace_test native library");
- }
- }
-
@Before
public void setUp() {
- PerfettoTrace.register();
- nativeRegisterPerfetto();
+ PerfettoTrace.register(true);
FOO_CATEGORY.register();
mCategoryNames.clear();
@@ -110,7 +99,7 @@ public class PerfettoTraceTest {
public void testDebugAnnotations() throws Exception {
TraceConfig traceConfig = getTraceConfig(FOO);
- long ptr = nativeStartTracing(traceConfig.toByteArray());
+ PerfettoTrace.Session session = new PerfettoTrace.Session(true, traceConfig.toByteArray());
PerfettoTrace.instant(FOO_CATEGORY, "event")
.addFlow(2)
@@ -121,7 +110,7 @@ public class PerfettoTraceTest {
.addArg("string_val", FOO)
.emit();
- byte[] traceBytes = nativeStopTracing(ptr);
+ byte[] traceBytes = session.close();
Trace trace = Trace.parseFrom(traceBytes);
@@ -165,11 +154,11 @@ public class PerfettoTraceTest {
public void testDebugAnnotationsWithLambda() throws Exception {
TraceConfig traceConfig = getTraceConfig(FOO);
- long ptr = nativeStartTracing(traceConfig.toByteArray());
+ PerfettoTrace.Session session = new PerfettoTrace.Session(true, traceConfig.toByteArray());
PerfettoTrace.instant(FOO_CATEGORY, "event").addArg("long_val", 123L).emit();
- byte[] traceBytes = nativeStopTracing(ptr);
+ byte[] traceBytes = session.close();
Trace trace = Trace.parseFrom(traceBytes);
@@ -200,7 +189,7 @@ public class PerfettoTraceTest {
public void testNamedTrack() throws Exception {
TraceConfig traceConfig = getTraceConfig(FOO);
- long ptr = nativeStartTracing(traceConfig.toByteArray());
+ PerfettoTrace.Session session = new PerfettoTrace.Session(true, traceConfig.toByteArray());
PerfettoTrace.begin(FOO_CATEGORY, "event")
.usingNamedTrack(PerfettoTrace.getProcessTrackUuid(), FOO)
@@ -211,7 +200,7 @@ public class PerfettoTraceTest {
.usingNamedTrack(PerfettoTrace.getThreadTrackUuid(Process.myTid()), "bar")
.emit();
- Trace trace = Trace.parseFrom(nativeStopTracing(ptr));
+ Trace trace = Trace.parseFrom(session.close());
boolean hasTrackEvent = false;
boolean hasTrackUuid = false;
@@ -248,7 +237,7 @@ public class PerfettoTraceTest {
public void testProcessThreadNamedTrack() throws Exception {
TraceConfig traceConfig = getTraceConfig(FOO);
- long ptr = nativeStartTracing(traceConfig.toByteArray());
+ PerfettoTrace.Session session = new PerfettoTrace.Session(true, traceConfig.toByteArray());
PerfettoTrace.begin(FOO_CATEGORY, "event")
.usingProcessNamedTrack(FOO)
@@ -259,7 +248,7 @@ public class PerfettoTraceTest {
.usingThreadNamedTrack(Process.myTid(), "%s-%s", "bar", "stool")
.emit();
- Trace trace = Trace.parseFrom(nativeStopTracing(ptr));
+ Trace trace = Trace.parseFrom(session.close());
boolean hasTrackEvent = false;
boolean hasTrackUuid = false;
@@ -296,13 +285,13 @@ public class PerfettoTraceTest {
public void testCounterSimple() throws Exception {
TraceConfig traceConfig = getTraceConfig(FOO);
- long ptr = nativeStartTracing(traceConfig.toByteArray());
+ PerfettoTrace.Session session = new PerfettoTrace.Session(true, traceConfig.toByteArray());
PerfettoTrace.counter(FOO_CATEGORY, 16, FOO).emit();
PerfettoTrace.counter(FOO_CATEGORY, 3.14, "bar").emit();
- Trace trace = Trace.parseFrom(nativeStopTracing(ptr));
+ Trace trace = Trace.parseFrom(session.close());
boolean hasTrackEvent = false;
boolean hasCounterValue = false;
@@ -339,7 +328,7 @@ public class PerfettoTraceTest {
public void testCounter() throws Exception {
TraceConfig traceConfig = getTraceConfig(FOO);
- long ptr = nativeStartTracing(traceConfig.toByteArray());
+ PerfettoTrace.Session session = new PerfettoTrace.Session(true, traceConfig.toByteArray());
PerfettoTrace.counter(FOO_CATEGORY, 16)
.usingCounterTrack(PerfettoTrace.getProcessTrackUuid(), FOO).emit();
@@ -348,7 +337,7 @@ public class PerfettoTraceTest {
.usingCounterTrack(PerfettoTrace.getThreadTrackUuid(Process.myTid()),
"%s-%s", "bar", "stool").emit();
- Trace trace = Trace.parseFrom(nativeStopTracing(ptr));
+ Trace trace = Trace.parseFrom(session.close());
boolean hasTrackEvent = false;
boolean hasCounterValue = false;
@@ -385,14 +374,14 @@ public class PerfettoTraceTest {
public void testProcessThreadCounter() throws Exception {
TraceConfig traceConfig = getTraceConfig(FOO);
- long ptr = nativeStartTracing(traceConfig.toByteArray());
+ PerfettoTrace.Session session = new PerfettoTrace.Session(true, traceConfig.toByteArray());
PerfettoTrace.counter(FOO_CATEGORY, 16).usingProcessCounterTrack(FOO).emit();
PerfettoTrace.counter(FOO_CATEGORY, 3.14)
.usingThreadCounterTrack(Process.myTid(), "%s-%s", "bar", "stool").emit();
- Trace trace = Trace.parseFrom(nativeStopTracing(ptr));
+ Trace trace = Trace.parseFrom(session.close());
boolean hasTrackEvent = false;
boolean hasCounterValue = false;
@@ -429,7 +418,7 @@ public class PerfettoTraceTest {
public void testProto() throws Exception {
TraceConfig traceConfig = getTraceConfig(FOO);
- long ptr = nativeStartTracing(traceConfig.toByteArray());
+ PerfettoTrace.Session session = new PerfettoTrace.Session(true, traceConfig.toByteArray());
PerfettoTrace.instant(FOO_CATEGORY, "event_proto")
.beginProto()
@@ -441,7 +430,7 @@ public class PerfettoTraceTest {
.endProto()
.emit();
- byte[] traceBytes = nativeStopTracing(ptr);
+ byte[] traceBytes = session.close();
Trace trace = Trace.parseFrom(traceBytes);
@@ -477,7 +466,7 @@ public class PerfettoTraceTest {
public void testProtoNested() throws Exception {
TraceConfig traceConfig = getTraceConfig(FOO);
- long ptr = nativeStartTracing(traceConfig.toByteArray());
+ PerfettoTrace.Session session = new PerfettoTrace.Session(true, traceConfig.toByteArray());
PerfettoTrace.instant(FOO_CATEGORY, "event_proto_nested")
.beginProto()
@@ -494,7 +483,7 @@ public class PerfettoTraceTest {
.endProto()
.emit();
- byte[] traceBytes = nativeStopTracing(ptr);
+ byte[] traceBytes = session.close();
Trace trace = Trace.parseFrom(traceBytes);
@@ -538,13 +527,13 @@ public class PerfettoTraceTest {
public void testActivateTrigger() throws Exception {
TraceConfig traceConfig = getTriggerTraceConfig(FOO, FOO);
- long ptr = nativeStartTracing(traceConfig.toByteArray());
+ PerfettoTrace.Session session = new PerfettoTrace.Session(true, traceConfig.toByteArray());
PerfettoTrace.instant(FOO_CATEGORY, "event_trigger").emit();
PerfettoTrace.activateTrigger(FOO, 1000);
- byte[] traceBytes = nativeStopTracing(ptr);
+ byte[] traceBytes = session.close();
Trace trace = Trace.parseFrom(traceBytes);
@@ -569,7 +558,7 @@ public class PerfettoTraceTest {
TraceConfig traceConfig = getTraceConfig(BAR);
Category barCategory = new Category(BAR);
- long ptr = nativeStartTracing(traceConfig.toByteArray());
+ PerfettoTrace.Session session = new PerfettoTrace.Session(true, traceConfig.toByteArray());
PerfettoTrace.instant(barCategory, "event")
.addArg("before", 1)
@@ -581,7 +570,7 @@ public class PerfettoTraceTest {
.addArg("after", 1)
.emit();
- byte[] traceBytes = nativeStopTracing(ptr);
+ byte[] traceBytes = session.close();
Trace trace = Trace.parseFrom(traceBytes);
@@ -603,10 +592,6 @@ public class PerfettoTraceTest {
assertThat(mDebugAnnotationNames).doesNotContain("before");
}
- private static native long nativeStartTracing(byte[] config);
- private static native void nativeRegisterPerfetto();
- private static native byte[] nativeStopTracing(long ptr);
-
private TrackEvent getTrackEvent(Trace trace, int idx) {
int curIdx = 0;
for (TracePacket packet: trace.getPacketList()) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
index b09d324833e8..4eaf8049f04f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
@@ -976,11 +976,13 @@ class DesktopTasksController(
cascadeWindow(bounds, displayLayout, displayId)
}
val pendingIntent =
- PendingIntent.getActivity(
+ PendingIntent.getActivityAsUser(
context,
/* requestCode= */ 0,
intent,
PendingIntent.FLAG_IMMUTABLE,
+ /* options= */ null,
+ UserHandle.of(userId),
)
val ops =
ActivityOptions.fromBundle(options).apply {
diff --git a/media/java/android/media/OWNERS b/media/java/android/media/OWNERS
index b6096a1acd85..a600017119e2 100644
--- a/media/java/android/media/OWNERS
+++ b/media/java/android/media/OWNERS
@@ -1,6 +1,7 @@
# Bug component: 1344
-fgoldfain@google.com
+pshehane@google.com
elaurent@google.com
+etalvala@google.com
lajos@google.com
jmtrivi@google.com
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index dace50fafbf1..a66ad19d5bd6 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -196,6 +196,18 @@ flag {
}
flag {
+ name: "notification_undo_guts_on_config_changed"
+ namespace: "systemui"
+ description: "Fixes a bug where a theme or font change while notification guts were open"
+ " (e.g. the snooze options or notification info) would show an empty notification by"
+ " closing the guts and undoing changes."
+ bug: "379267630"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "pss_app_selector_recents_split_screen"
namespace: "systemui"
description: "Allows recent apps selected for partial screenshare to be launched in split screen mode"
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContent.kt
index 30dfa5bb826a..31aebc28d072 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContent.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContent.kt
@@ -156,14 +156,8 @@ constructor(
val bottomAreaPlaceable = bottomAreaMeasurable.measure(noMinConstraints)
- val screensaverButtonSizeInt = screensaverButtonSize.roundToPx()
val screensaverButtonPlaceable =
- screensaverButtonMeasurable?.measure(
- Constraints.fixed(
- width = screensaverButtonSizeInt,
- height = screensaverButtonSizeInt,
- )
- )
+ screensaverButtonMeasurable?.measure(noMinConstraints)
val communalGridPlaceable =
communalGridMeasurable.measure(
@@ -181,12 +175,12 @@ constructor(
screensaverButtonPlaceable?.place(
x =
constraints.maxWidth -
- screensaverButtonSizeInt -
- screensaverButtonPaddingInt,
+ screensaverButtonPaddingInt -
+ screensaverButtonPlaceable.width,
y =
constraints.maxHeight -
- screensaverButtonSizeInt -
- screensaverButtonPaddingInt,
+ screensaverButtonPaddingInt -
+ screensaverButtonPlaceable.height,
)
}
}
@@ -194,7 +188,6 @@ constructor(
}
companion object {
- private val screensaverButtonSize: Dp = 64.dp
private val screensaverButtonPadding: Dp = 24.dp
// TODO(b/382739998): Remove these hardcoded values once lock icon size and bottom area
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/section/CommunalToDreamButtonSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/section/CommunalToDreamButtonSection.kt
index 9421596f7116..13d551aef4c2 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/section/CommunalToDreamButtonSection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/section/CommunalToDreamButtonSection.kt
@@ -16,14 +16,37 @@
package com.android.systemui.communal.ui.compose.section
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.layout.widthIn
+import androidx.compose.foundation.shape.CornerSize
import androidx.compose.material3.IconButtonDefaults
import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Surface
+import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.getValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.geometry.CornerRadius
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.geometry.RoundRect
+import androidx.compose.ui.geometry.Size
+import androidx.compose.ui.geometry.toRect
+import androidx.compose.ui.graphics.Outline
+import androidx.compose.ui.graphics.Path
+import androidx.compose.ui.graphics.Shape
+import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.res.stringResource
-import androidx.lifecycle.compose.collectAsStateWithLifecycle
+import androidx.compose.ui.unit.Density
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.LayoutDirection
+import androidx.compose.ui.unit.dp
import com.android.compose.PlatformIconButton
import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor
+import com.android.systemui.communal.ui.compose.extensions.observeTaps
import com.android.systemui.communal.ui.viewmodel.CommunalToDreamButtonViewModel
import com.android.systemui.lifecycle.rememberViewModel
import com.android.systemui.res.R
@@ -43,23 +66,111 @@ constructor(
val viewModel =
rememberViewModel("CommunalToDreamButtonSection") { viewModelFactory.create() }
- val shouldShowDreamButtonOnHub by
- viewModel.shouldShowDreamButtonOnHub.collectAsStateWithLifecycle(false)
- if (!shouldShowDreamButtonOnHub) {
+ if (!viewModel.shouldShowDreamButtonOnHub) {
return
}
- PlatformIconButton(
- onClick = { viewModel.onShowDreamButtonTap() },
- iconResource = R.drawable.ic_screensaver_auto,
- contentDescription =
- stringResource(R.string.accessibility_glanceable_hub_to_dream_button),
- colors =
- IconButtonDefaults.filledIconButtonColors(
- contentColor = MaterialTheme.colorScheme.onPrimaryContainer,
- containerColor = MaterialTheme.colorScheme.primaryContainer,
- ),
+ if (viewModel.shouldShowTooltip) {
+ Column(
+ modifier =
+ Modifier.widthIn(max = tooltipMaxWidth).pointerInput(Unit) {
+ observeTaps { viewModel.setDreamButtonTooltipDismissed() }
+ }
+ ) {
+ Tooltip(
+ pointerOffsetDp = buttonSize.div(2),
+ text = stringResource(R.string.glanceable_hub_to_dream_button_tooltip),
+ )
+ GoToDreamButton(
+ modifier = Modifier.width(buttonSize).height(buttonSize).align(Alignment.End)
+ ) {
+ viewModel.onShowDreamButtonTap()
+ }
+ }
+ } else {
+ GoToDreamButton(modifier = Modifier.width(buttonSize).height(buttonSize)) {
+ viewModel.onShowDreamButtonTap()
+ }
+ }
+ }
+
+ companion object {
+ private val buttonSize = 64.dp
+ private val tooltipMaxWidth = 350.dp
+ }
+}
+
+@Composable
+private fun GoToDreamButton(modifier: Modifier, onClick: () -> Unit) {
+ PlatformIconButton(
+ modifier = modifier,
+ onClick = onClick,
+ iconResource = R.drawable.ic_screensaver_auto,
+ contentDescription = stringResource(R.string.accessibility_glanceable_hub_to_dream_button),
+ colors =
+ IconButtonDefaults.filledIconButtonColors(
+ contentColor = MaterialTheme.colorScheme.onPrimaryContainer,
+ containerColor = MaterialTheme.colorScheme.primaryContainer,
+ ),
+ )
+}
+
+@Composable
+private fun Tooltip(pointerOffsetDp: Dp, text: String) {
+ Surface(
+ color = MaterialTheme.colorScheme.surface,
+ shape = TooltipShape(pointerSizeDp = 12.dp, pointerOffsetDp = pointerOffsetDp),
+ ) {
+ Text(
+ modifier = Modifier.padding(start = 32.dp, top = 16.dp, end = 32.dp, bottom = 32.dp),
+ color = MaterialTheme.colorScheme.onSurface,
+ text = text,
)
}
+
+ Spacer(modifier = Modifier.height(4.dp))
+}
+
+private class TooltipShape(private val pointerSizeDp: Dp, private val pointerOffsetDp: Dp) : Shape {
+
+ override fun createOutline(
+ size: Size,
+ layoutDirection: LayoutDirection,
+ density: Density,
+ ): Outline {
+
+ val pointerSizePx = with(density) { pointerSizeDp.toPx() }
+ val pointerOffsetPx = with(density) { pointerOffsetDp.toPx() }
+ val cornerRadius = CornerRadius(CornerSize(16.dp).toPx(size, density))
+ val bubbleSize = size.copy(height = size.height - pointerSizePx)
+
+ val path =
+ Path().apply {
+ addRoundRect(
+ RoundRect(
+ rect = bubbleSize.toRect(),
+ topLeft = cornerRadius,
+ topRight = cornerRadius,
+ bottomRight = cornerRadius,
+ bottomLeft = cornerRadius,
+ )
+ )
+ addPath(
+ Path().apply {
+ moveTo(0f, 0f)
+ lineTo(pointerSizePx / 2f, pointerSizePx)
+ lineTo(pointerSizePx, 0f)
+ close()
+ },
+ offset =
+ Offset(
+ x = bubbleSize.width - pointerOffsetPx - pointerSizePx / 2f,
+ y = bubbleSize.height,
+ ),
+ )
+ }
+
+ return Outline.Generic(path)
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalPrefsRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalPrefsRepositoryImplTest.kt
index 0d410cff5ff6..b6359c7f8da5 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalPrefsRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalPrefsRepositoryImplTest.kt
@@ -116,6 +116,34 @@ class CommunalPrefsRepositoryImplTest : SysuiTestCase() {
}
@Test
+ fun isDreamButtonTooltipDismissedValue_byDefault_isFalse() =
+ testScope.runTest {
+ val isDreamButtonTooltipDismissed by
+ collectLastValue(underTest.isDreamButtonTooltipDismissed(MAIN_USER))
+ assertThat(isDreamButtonTooltipDismissed).isFalse()
+ }
+
+ @Test
+ fun isDreamButtonTooltipDismissedValue_onSet_isTrue() =
+ testScope.runTest {
+ val isDreamButtonTooltipDismissed by
+ collectLastValue(underTest.isDreamButtonTooltipDismissed(MAIN_USER))
+
+ underTest.setDreamButtonTooltipDismissed(MAIN_USER)
+ assertThat(isDreamButtonTooltipDismissed).isTrue()
+ }
+
+ @Test
+ fun isDreamButtonTooltipDismissedValue_onSetForDifferentUser_isStillFalse() =
+ testScope.runTest {
+ val isDreamButtonTooltipDismissed by
+ collectLastValue(underTest.isDreamButtonTooltipDismissed(MAIN_USER))
+
+ underTest.setDreamButtonTooltipDismissed(SECONDARY_USER)
+ assertThat(isDreamButtonTooltipDismissed).isFalse()
+ }
+
+ @Test
fun getSharedPreferences_whenFileRestored() =
testScope.runTest {
val isCtaDismissed by collectLastValue(underTest.isCtaDismissed(MAIN_USER))
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalSettingsRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalSettingsRepositoryImplTest.kt
index 809099e0d464..eb1f1d9c52f4 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalSettingsRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalSettingsRepositoryImplTest.kt
@@ -38,19 +38,19 @@ import com.android.systemui.communal.data.model.DisabledReason
import com.android.systemui.communal.data.repository.CommunalSettingsRepositoryImpl.Companion.GLANCEABLE_HUB_BACKGROUND_SETTING
import com.android.systemui.communal.domain.interactor.setCommunalV2Enabled
import com.android.systemui.communal.shared.model.CommunalBackgroundType
-import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.communal.shared.model.WhenToDream
import com.android.systemui.flags.Flags.COMMUNAL_SERVICE_ENABLED
import com.android.systemui.flags.fakeFeatureFlagsClassic
+import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.collectLastValue
import com.android.systemui.kosmos.runTest
-import com.android.systemui.kosmos.testScope
+import com.android.systemui.kosmos.useUnconfinedTestDispatcher
import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.nullable
import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.settings.fakeSettings
import com.google.common.truth.Truth.assertThat
import com.google.common.truth.Truth.assertWithMessage
-import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -62,9 +62,11 @@ import platform.test.runner.parameterized.Parameters
@RunWith(ParameterizedAndroidJunit4::class)
class CommunalSettingsRepositoryImplTest(flags: FlagsParameterization?) : SysuiTestCase() {
private val kosmos =
- testKosmos().apply { mainResources = mContext.orCreateTestableResources.resources }
- private val testScope = kosmos.testScope
- private lateinit var underTest: CommunalSettingsRepository
+ testKosmos()
+ .apply { mainResources = mContext.orCreateTestableResources.resources }
+ .useUnconfinedTestDispatcher()
+
+ private val Kosmos.underTest by Kosmos.Fixture { communalSettingsRepository }
init {
mSetFlagsRule.setFlagsParameterization(flags!!)
@@ -76,98 +78,105 @@ class CommunalSettingsRepositoryImplTest(flags: FlagsParameterization?) : SysuiT
setKeyguardFeaturesDisabled(PRIMARY_USER, KEYGUARD_DISABLE_FEATURES_NONE)
setKeyguardFeaturesDisabled(SECONDARY_USER, KEYGUARD_DISABLE_FEATURES_NONE)
setKeyguardFeaturesDisabled(WORK_PROFILE, KEYGUARD_DISABLE_FEATURES_NONE)
- underTest = kosmos.communalSettingsRepository
}
@EnableFlags(FLAG_COMMUNAL_HUB)
@DisableFlags(FLAG_GLANCEABLE_HUB_V2)
@Test
- fun getFlagEnabled_bothEnabled() {
- kosmos.fakeFeatureFlagsClassic.set(COMMUNAL_SERVICE_ENABLED, true)
+ fun getFlagEnabled_bothEnabled() =
+ kosmos.runTest {
+ fakeFeatureFlagsClassic.set(COMMUNAL_SERVICE_ENABLED, true)
- assertThat(underTest.getFlagEnabled()).isTrue()
- }
+ assertThat(underTest.getFlagEnabled()).isTrue()
+ }
@DisableFlags(FLAG_COMMUNAL_HUB, FLAG_GLANCEABLE_HUB_V2)
@Test
- fun getFlagEnabled_bothDisabled() {
- kosmos.fakeFeatureFlagsClassic.set(COMMUNAL_SERVICE_ENABLED, false)
+ fun getFlagEnabled_bothDisabled() =
+ kosmos.runTest {
+ fakeFeatureFlagsClassic.set(COMMUNAL_SERVICE_ENABLED, false)
- assertThat(underTest.getFlagEnabled()).isFalse()
- }
+ assertThat(underTest.getFlagEnabled()).isFalse()
+ }
@DisableFlags(FLAG_COMMUNAL_HUB, FLAG_GLANCEABLE_HUB_V2)
@Test
- fun getFlagEnabled_onlyClassicFlagEnabled() {
- kosmos.fakeFeatureFlagsClassic.set(COMMUNAL_SERVICE_ENABLED, true)
+ fun getFlagEnabled_onlyClassicFlagEnabled() =
+ kosmos.runTest {
+ fakeFeatureFlagsClassic.set(COMMUNAL_SERVICE_ENABLED, true)
- assertThat(underTest.getFlagEnabled()).isFalse()
- }
+ assertThat(underTest.getFlagEnabled()).isFalse()
+ }
@EnableFlags(FLAG_COMMUNAL_HUB)
@DisableFlags(FLAG_GLANCEABLE_HUB_V2)
@Test
- fun getFlagEnabled_onlyTrunkFlagEnabled() {
- kosmos.fakeFeatureFlagsClassic.set(COMMUNAL_SERVICE_ENABLED, false)
+ fun getFlagEnabled_onlyTrunkFlagEnabled() =
+ kosmos.runTest {
+ fakeFeatureFlagsClassic.set(COMMUNAL_SERVICE_ENABLED, false)
- assertThat(underTest.getFlagEnabled()).isFalse()
- }
+ assertThat(underTest.getFlagEnabled()).isFalse()
+ }
@EnableFlags(FLAG_GLANCEABLE_HUB_V2)
@DisableFlags(FLAG_COMMUNAL_HUB)
@Test
- fun getFlagEnabled_mobileConfigEnabled() {
- mContext.orCreateTestableResources.addOverride(
- com.android.internal.R.bool.config_glanceableHubEnabled,
- true,
- )
+ fun getFlagEnabled_mobileConfigEnabled() =
+ kosmos.runTest {
+ mContext.orCreateTestableResources.addOverride(
+ com.android.internal.R.bool.config_glanceableHubEnabled,
+ true,
+ )
- assertThat(underTest.getFlagEnabled()).isTrue()
- }
+ assertThat(underTest.getFlagEnabled()).isTrue()
+ }
@DisableFlags(FLAG_GLANCEABLE_HUB_V2, FLAG_COMMUNAL_HUB)
@Test
- fun getFlagEnabled_onlyMobileConfigEnabled() {
- mContext.orCreateTestableResources.addOverride(
- com.android.internal.R.bool.config_glanceableHubEnabled,
- true,
- )
+ fun getFlagEnabled_onlyMobileConfigEnabled() =
+ kosmos.runTest {
+ mContext.orCreateTestableResources.addOverride(
+ com.android.internal.R.bool.config_glanceableHubEnabled,
+ true,
+ )
- assertThat(underTest.getFlagEnabled()).isFalse()
- }
+ assertThat(underTest.getFlagEnabled()).isFalse()
+ }
@EnableFlags(FLAG_GLANCEABLE_HUB_V2)
@DisableFlags(FLAG_COMMUNAL_HUB)
@Test
- fun getFlagEnabled_onlyMobileFlagEnabled() {
- mContext.orCreateTestableResources.addOverride(
- com.android.internal.R.bool.config_glanceableHubEnabled,
- false,
- )
+ fun getFlagEnabled_onlyMobileFlagEnabled() =
+ kosmos.runTest {
+ mContext.orCreateTestableResources.addOverride(
+ com.android.internal.R.bool.config_glanceableHubEnabled,
+ false,
+ )
- assertThat(underTest.getFlagEnabled()).isFalse()
- }
+ assertThat(underTest.getFlagEnabled()).isFalse()
+ }
@EnableFlags(FLAG_GLANCEABLE_HUB_V2)
@DisableFlags(FLAG_COMMUNAL_HUB)
@Test
- fun getFlagEnabled_oldFlagIgnored() {
- // New config flag enabled.
- mContext.orCreateTestableResources.addOverride(
- com.android.internal.R.bool.config_glanceableHubEnabled,
- true,
- )
+ fun getFlagEnabled_oldFlagIgnored() =
+ kosmos.runTest {
+ // New config flag enabled.
+ mContext.orCreateTestableResources.addOverride(
+ com.android.internal.R.bool.config_glanceableHubEnabled,
+ true,
+ )
- // Old config flag disabled.
- kosmos.fakeFeatureFlagsClassic.set(COMMUNAL_SERVICE_ENABLED, false)
+ // Old config flag disabled.
+ fakeFeatureFlagsClassic.set(COMMUNAL_SERVICE_ENABLED, false)
- assertThat(underTest.getFlagEnabled()).isTrue()
- }
+ assertThat(underTest.getFlagEnabled()).isTrue()
+ }
@EnableFlags(FLAG_COMMUNAL_HUB)
@Test
fun secondaryUserIsInvalid() =
- testScope.runTest {
+ kosmos.runTest {
val enabledState by collectLastValue(underTest.getEnabledState(SECONDARY_USER))
assertThat(enabledState?.enabled).isFalse()
@@ -187,7 +196,7 @@ class CommunalSettingsRepositoryImplTest(flags: FlagsParameterization?) : SysuiT
@DisableFlags(FLAG_COMMUNAL_HUB, FLAG_GLANCEABLE_HUB_V2)
@Test
fun communalHubFlagIsDisabled() =
- testScope.runTest {
+ kosmos.runTest {
val enabledState by collectLastValue(underTest.getEnabledState(PRIMARY_USER))
assertThat(enabledState?.enabled).isFalse()
assertThat(enabledState).containsExactly(DisabledReason.DISABLED_REASON_FLAG)
@@ -196,35 +205,23 @@ class CommunalSettingsRepositoryImplTest(flags: FlagsParameterization?) : SysuiT
@EnableFlags(FLAG_COMMUNAL_HUB)
@Test
fun hubIsDisabledByUser() =
- testScope.runTest {
- kosmos.fakeSettings.putIntForUser(
- Settings.Secure.GLANCEABLE_HUB_ENABLED,
- 0,
- PRIMARY_USER.id,
- )
+ kosmos.runTest {
+ fakeSettings.putIntForUser(Settings.Secure.GLANCEABLE_HUB_ENABLED, 0, PRIMARY_USER.id)
val enabledState by collectLastValue(underTest.getEnabledState(PRIMARY_USER))
assertThat(enabledState?.enabled).isFalse()
assertThat(enabledState).containsExactly(DisabledReason.DISABLED_REASON_USER_SETTING)
- kosmos.fakeSettings.putIntForUser(
- Settings.Secure.GLANCEABLE_HUB_ENABLED,
- 1,
- SECONDARY_USER.id,
- )
+ fakeSettings.putIntForUser(Settings.Secure.GLANCEABLE_HUB_ENABLED, 1, SECONDARY_USER.id)
assertThat(enabledState?.enabled).isFalse()
- kosmos.fakeSettings.putIntForUser(
- Settings.Secure.GLANCEABLE_HUB_ENABLED,
- 1,
- PRIMARY_USER.id,
- )
+ fakeSettings.putIntForUser(Settings.Secure.GLANCEABLE_HUB_ENABLED, 1, PRIMARY_USER.id)
assertThat(enabledState?.enabled).isTrue()
}
@EnableFlags(FLAG_COMMUNAL_HUB)
@Test
fun hubIsDisabledByDevicePolicy() =
- testScope.runTest {
+ kosmos.runTest {
val enabledState by collectLastValue(underTest.getEnabledState(PRIMARY_USER))
assertThat(enabledState?.enabled).isTrue()
@@ -236,7 +233,7 @@ class CommunalSettingsRepositoryImplTest(flags: FlagsParameterization?) : SysuiT
@EnableFlags(FLAG_COMMUNAL_HUB)
@Test
fun widgetsAllowedForWorkProfile_isFalse_whenDisallowedByDevicePolicy() =
- testScope.runTest {
+ kosmos.runTest {
val widgetsAllowedForWorkProfile by
collectLastValue(underTest.getAllowedByDevicePolicy(WORK_PROFILE))
assertThat(widgetsAllowedForWorkProfile).isTrue()
@@ -248,7 +245,7 @@ class CommunalSettingsRepositoryImplTest(flags: FlagsParameterization?) : SysuiT
@EnableFlags(FLAG_COMMUNAL_HUB)
@Test
fun hubIsEnabled_whenDisallowedByDevicePolicyForWorkProfile() =
- testScope.runTest {
+ kosmos.runTest {
val enabledStateForPrimaryUser by
collectLastValue(underTest.getEnabledState(PRIMARY_USER))
assertThat(enabledStateForPrimaryUser?.enabled).isTrue()
@@ -260,15 +257,11 @@ class CommunalSettingsRepositoryImplTest(flags: FlagsParameterization?) : SysuiT
@EnableFlags(FLAG_COMMUNAL_HUB)
@Test
fun hubIsDisabledByUserAndDevicePolicy() =
- testScope.runTest {
+ kosmos.runTest {
val enabledState by collectLastValue(underTest.getEnabledState(PRIMARY_USER))
assertThat(enabledState?.enabled).isTrue()
- kosmos.fakeSettings.putIntForUser(
- Settings.Secure.GLANCEABLE_HUB_ENABLED,
- 0,
- PRIMARY_USER.id,
- )
+ fakeSettings.putIntForUser(Settings.Secure.GLANCEABLE_HUB_ENABLED, 0, PRIMARY_USER.id)
setKeyguardFeaturesDisabled(PRIMARY_USER, KEYGUARD_DISABLE_WIDGETS_ALL)
assertThat(enabledState?.enabled).isFalse()
@@ -282,17 +275,17 @@ class CommunalSettingsRepositoryImplTest(flags: FlagsParameterization?) : SysuiT
@Test
@DisableFlags(FLAG_GLANCEABLE_HUB_BLURRED_BACKGROUND)
fun backgroundType_defaultValue() =
- testScope.runTest {
+ kosmos.runTest {
val backgroundType by collectLastValue(underTest.getBackground(PRIMARY_USER))
assertThat(backgroundType).isEqualTo(CommunalBackgroundType.ANIMATED)
}
@Test
fun backgroundType_verifyAllValues() =
- testScope.runTest {
+ kosmos.runTest {
val backgroundType by collectLastValue(underTest.getBackground(PRIMARY_USER))
for (type in CommunalBackgroundType.entries) {
- kosmos.fakeSettings.putIntForUser(
+ fakeSettings.putIntForUser(
GLANCEABLE_HUB_BACKGROUND_SETTING,
type.value,
PRIMARY_USER.id,
@@ -308,30 +301,71 @@ class CommunalSettingsRepositoryImplTest(flags: FlagsParameterization?) : SysuiT
@Test
fun screensaverDisabledByUser() =
- testScope.runTest {
+ kosmos.runTest {
val enabledState by collectLastValue(underTest.getScreensaverEnabledState(PRIMARY_USER))
- kosmos.fakeSettings.putIntForUser(
- Settings.Secure.SCREENSAVER_ENABLED,
- 0,
- PRIMARY_USER.id,
- )
+ fakeSettings.putIntForUser(Settings.Secure.SCREENSAVER_ENABLED, 0, PRIMARY_USER.id)
assertThat(enabledState).isFalse()
}
@Test
fun screensaverEnabledByUser() =
- testScope.runTest {
+ kosmos.runTest {
val enabledState by collectLastValue(underTest.getScreensaverEnabledState(PRIMARY_USER))
- kosmos.fakeSettings.putIntForUser(
- Settings.Secure.SCREENSAVER_ENABLED,
+ fakeSettings.putIntForUser(Settings.Secure.SCREENSAVER_ENABLED, 1, PRIMARY_USER.id)
+
+ assertThat(enabledState).isTrue()
+ }
+
+ @Test
+ fun whenToDream_charging() =
+ kosmos.runTest {
+ val whenToDreamState by collectLastValue(underTest.getWhenToDreamState(PRIMARY_USER))
+
+ fakeSettings.putIntForUser(
+ Settings.Secure.SCREENSAVER_ACTIVATE_ON_SLEEP,
1,
PRIMARY_USER.id,
)
- assertThat(enabledState).isTrue()
+ assertThat(whenToDreamState).isEqualTo(WhenToDream.WHILE_CHARGING)
+ }
+
+ @Test
+ fun whenToDream_docked() =
+ kosmos.runTest {
+ val whenToDreamState by collectLastValue(underTest.getWhenToDreamState(PRIMARY_USER))
+
+ fakeSettings.putIntForUser(
+ Settings.Secure.SCREENSAVER_ACTIVATE_ON_DOCK,
+ 1,
+ PRIMARY_USER.id,
+ )
+
+ assertThat(whenToDreamState).isEqualTo(WhenToDream.WHILE_DOCKED)
+ }
+
+ @Test
+ fun whenToDream_postured() =
+ kosmos.runTest {
+ val whenToDreamState by collectLastValue(underTest.getWhenToDreamState(PRIMARY_USER))
+
+ fakeSettings.putIntForUser(
+ Settings.Secure.SCREENSAVER_ACTIVATE_ON_POSTURED,
+ 1,
+ PRIMARY_USER.id,
+ )
+
+ assertThat(whenToDreamState).isEqualTo(WhenToDream.WHILE_POSTURED)
+ }
+
+ @Test
+ fun whenToDream_default() =
+ kosmos.runTest {
+ val whenToDreamState by collectLastValue(underTest.getWhenToDreamState(PRIMARY_USER))
+ assertThat(whenToDreamState).isEqualTo(WhenToDream.NEVER)
}
private fun setKeyguardFeaturesDisabled(user: UserInfo, disabledFlags: Int) {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
index 7ae0577bd289..c9e7a5d7df05 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
@@ -38,13 +38,9 @@ import com.android.systemui.Flags.FLAG_COMMUNAL_WIDGET_RESIZING
import com.android.systemui.Flags.FLAG_GLANCEABLE_HUB_V2
import com.android.systemui.SysuiTestCase
import com.android.systemui.broadcast.broadcastDispatcher
+import com.android.systemui.common.data.repository.batteryRepository
+import com.android.systemui.common.data.repository.fake
import com.android.systemui.communal.data.model.CommunalSmartspaceTimer
-import com.android.systemui.communal.data.repository.FakeCommunalMediaRepository
-import com.android.systemui.communal.data.repository.FakeCommunalPrefsRepository
-import com.android.systemui.communal.data.repository.FakeCommunalSceneRepository
-import com.android.systemui.communal.data.repository.FakeCommunalSmartspaceRepository
-import com.android.systemui.communal.data.repository.FakeCommunalTutorialRepository
-import com.android.systemui.communal.data.repository.FakeCommunalWidgetRepository
import com.android.systemui.communal.data.repository.fakeCommunalMediaRepository
import com.android.systemui.communal.data.repository.fakeCommunalPrefsRepository
import com.android.systemui.communal.data.repository.fakeCommunalSceneRepository
@@ -53,52 +49,49 @@ import com.android.systemui.communal.data.repository.fakeCommunalTutorialReposit
import com.android.systemui.communal.data.repository.fakeCommunalWidgetRepository
import com.android.systemui.communal.domain.model.CommunalContentModel
import com.android.systemui.communal.domain.model.CommunalTransitionProgressModel
+import com.android.systemui.communal.posturing.data.repository.fake
+import com.android.systemui.communal.posturing.data.repository.posturingRepository
+import com.android.systemui.communal.posturing.shared.model.PosturedState
import com.android.systemui.communal.shared.model.CommunalContentSize
import com.android.systemui.communal.shared.model.CommunalScenes
import com.android.systemui.communal.shared.model.EditModeState
-import com.android.systemui.communal.widgets.EditWidgetsActivityStarter
-import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.dock.DockManager
+import com.android.systemui.dock.fakeDockManager
import com.android.systemui.flags.EnableSceneContainer
import com.android.systemui.flags.Flags
import com.android.systemui.flags.fakeFeatureFlagsClassic
-import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.collectLastValue
+import com.android.systemui.kosmos.runTest
import com.android.systemui.kosmos.testScope
-import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.kosmos.useUnconfinedTestDispatcher
import com.android.systemui.plugins.activityStarter
-import com.android.systemui.scene.domain.interactor.SceneInteractor
import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.scene.shared.model.Scenes
-import com.android.systemui.settings.FakeUserTracker
import com.android.systemui.settings.fakeUserTracker
import com.android.systemui.statusbar.phone.fakeManagedProfileController
import com.android.systemui.testKosmos
-import com.android.systemui.user.data.repository.FakeUserRepository
import com.android.systemui.user.data.repository.fakeUserRepository
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.argumentCaptor
import com.android.systemui.util.mockito.capture
import com.android.systemui.util.mockito.nullable
import com.android.systemui.util.mockito.whenever
-import com.android.systemui.utils.leaks.FakeManagedProfileController
+import com.android.systemui.util.settings.fakeSettings
import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.test.advanceTimeBy
-import kotlinx.coroutines.test.runCurrent
-import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.ArgumentMatchers.eq
-import org.mockito.Mock
import org.mockito.Mockito.mock
import org.mockito.Mockito.verify
-import org.mockito.MockitoAnnotations
import platform.test.runner.parameterized.ParameterizedAndroidJunit4
import platform.test.runner.parameterized.Parameters
@@ -107,32 +100,15 @@ import platform.test.runner.parameterized.Parameters
* [CommunalInteractorCommunalDisabledTest].
*/
@SmallTest
-@OptIn(ExperimentalCoroutinesApi::class)
@RunWith(ParameterizedAndroidJunit4::class)
class CommunalInteractorTest(flags: FlagsParameterization) : SysuiTestCase() {
- @Mock private lateinit var mainUser: UserInfo
- @Mock private lateinit var secondaryUser: UserInfo
-
- private val kosmos = testKosmos()
- private val testScope = kosmos.testScope
-
- private lateinit var tutorialRepository: FakeCommunalTutorialRepository
- private lateinit var communalRepository: FakeCommunalSceneRepository
- private lateinit var mediaRepository: FakeCommunalMediaRepository
- private lateinit var widgetRepository: FakeCommunalWidgetRepository
- private lateinit var smartspaceRepository: FakeCommunalSmartspaceRepository
- private lateinit var userRepository: FakeUserRepository
- private lateinit var keyguardRepository: FakeKeyguardRepository
- private lateinit var communalPrefsRepository: FakeCommunalPrefsRepository
- private lateinit var editWidgetsActivityStarter: EditWidgetsActivityStarter
- private lateinit var sceneInteractor: SceneInteractor
- private lateinit var communalSceneInteractor: CommunalSceneInteractor
- private lateinit var userTracker: FakeUserTracker
- private lateinit var activityStarter: ActivityStarter
- private lateinit var userManager: UserManager
- private lateinit var managedProfileController: FakeManagedProfileController
-
- private lateinit var underTest: CommunalInteractor
+ private val mainUser =
+ UserInfo(/* id= */ 0, /* name= */ "primary user", /* flags= */ UserInfo.FLAG_MAIN)
+ private val secondaryUser = UserInfo(/* id= */ 1, /* name= */ "secondary user", /* flags= */ 0)
+
+ private val kosmos = testKosmos().useUnconfinedTestDispatcher()
+
+ private val Kosmos.underTest by Kosmos.Fixture { communalInteractor }
init {
mSetFlagsRule.setFlagsParameterization(flags)
@@ -140,128 +116,104 @@ class CommunalInteractorTest(flags: FlagsParameterization) : SysuiTestCase() {
@Before
fun setUp() {
- MockitoAnnotations.initMocks(this)
-
- tutorialRepository = kosmos.fakeCommunalTutorialRepository
- communalRepository = kosmos.fakeCommunalSceneRepository
- mediaRepository = kosmos.fakeCommunalMediaRepository
- widgetRepository = kosmos.fakeCommunalWidgetRepository
- smartspaceRepository = kosmos.fakeCommunalSmartspaceRepository
- userRepository = kosmos.fakeUserRepository
- keyguardRepository = kosmos.fakeKeyguardRepository
- editWidgetsActivityStarter = kosmos.editWidgetsActivityStarter
- communalPrefsRepository = kosmos.fakeCommunalPrefsRepository
- sceneInteractor = kosmos.sceneInteractor
- communalSceneInteractor = kosmos.communalSceneInteractor
- userTracker = kosmos.fakeUserTracker
- activityStarter = kosmos.activityStarter
- userManager = kosmos.userManager
- managedProfileController = kosmos.fakeManagedProfileController
-
- whenever(mainUser.isMain).thenReturn(true)
- whenever(secondaryUser.isMain).thenReturn(false)
- whenever(userManager.isQuietModeEnabled(any<UserHandle>())).thenReturn(false)
- whenever(userManager.isManagedProfile(anyInt())).thenReturn(false)
- userRepository.setUserInfos(listOf(mainUser, secondaryUser))
+ whenever(kosmos.userManager.isQuietModeEnabled(any<UserHandle>())).thenReturn(false)
+ whenever(kosmos.userManager.isManagedProfile(anyInt())).thenReturn(false)
+ kosmos.fakeUserRepository.setUserInfos(listOf(mainUser, secondaryUser))
kosmos.fakeFeatureFlagsClassic.set(Flags.COMMUNAL_SERVICE_ENABLED, true)
mSetFlagsRule.enableFlags(FLAG_COMMUNAL_HUB)
-
- underTest = kosmos.communalInteractor
}
@Test
fun communalEnabled_true() =
- testScope.runTest {
- userRepository.setSelectedUserInfo(mainUser)
- runCurrent()
+ kosmos.runTest {
+ fakeUserRepository.setSelectedUserInfo(mainUser)
assertThat(underTest.isCommunalEnabled.value).isTrue()
}
@Test
fun isCommunalAvailable_storageUnlockedAndMainUser_true() =
- testScope.runTest {
+ kosmos.runTest {
val isAvailable by collectLastValue(underTest.isCommunalAvailable)
assertThat(isAvailable).isFalse()
- keyguardRepository.setIsEncryptedOrLockdown(false)
- userRepository.setSelectedUserInfo(mainUser)
- keyguardRepository.setKeyguardShowing(true)
+ fakeKeyguardRepository.setIsEncryptedOrLockdown(false)
+ fakeUserRepository.setSelectedUserInfo(mainUser)
+ fakeKeyguardRepository.setKeyguardShowing(true)
assertThat(isAvailable).isTrue()
}
@Test
fun isCommunalAvailable_storageLockedAndMainUser_false() =
- testScope.runTest {
+ kosmos.runTest {
val isAvailable by collectLastValue(underTest.isCommunalAvailable)
assertThat(isAvailable).isFalse()
- keyguardRepository.setIsEncryptedOrLockdown(true)
- userRepository.setSelectedUserInfo(mainUser)
- keyguardRepository.setKeyguardShowing(true)
+ fakeKeyguardRepository.setIsEncryptedOrLockdown(true)
+ fakeUserRepository.setSelectedUserInfo(mainUser)
+ fakeKeyguardRepository.setKeyguardShowing(true)
assertThat(isAvailable).isFalse()
}
@Test
fun isCommunalAvailable_storageUnlockedAndSecondaryUser_false() =
- testScope.runTest {
+ kosmos.runTest {
val isAvailable by collectLastValue(underTest.isCommunalAvailable)
assertThat(isAvailable).isFalse()
- keyguardRepository.setIsEncryptedOrLockdown(false)
- userRepository.setSelectedUserInfo(secondaryUser)
- keyguardRepository.setKeyguardShowing(true)
+ fakeKeyguardRepository.setIsEncryptedOrLockdown(false)
+ fakeUserRepository.setSelectedUserInfo(secondaryUser)
+ fakeKeyguardRepository.setKeyguardShowing(true)
assertThat(isAvailable).isFalse()
}
@Test
fun isCommunalAvailable_whenKeyguardShowing_true() =
- testScope.runTest {
+ kosmos.runTest {
val isAvailable by collectLastValue(underTest.isCommunalAvailable)
assertThat(isAvailable).isFalse()
- keyguardRepository.setIsEncryptedOrLockdown(false)
- userRepository.setSelectedUserInfo(mainUser)
- keyguardRepository.setKeyguardShowing(true)
+ fakeKeyguardRepository.setIsEncryptedOrLockdown(false)
+ fakeUserRepository.setSelectedUserInfo(mainUser)
+ fakeKeyguardRepository.setKeyguardShowing(true)
assertThat(isAvailable).isTrue()
}
@Test
fun isCommunalAvailable_communalDisabled_false() =
- testScope.runTest {
+ kosmos.runTest {
mSetFlagsRule.disableFlags(FLAG_COMMUNAL_HUB, FLAG_GLANCEABLE_HUB_V2)
val isAvailable by collectLastValue(underTest.isCommunalAvailable)
assertThat(isAvailable).isFalse()
- keyguardRepository.setIsEncryptedOrLockdown(false)
- userRepository.setSelectedUserInfo(mainUser)
- keyguardRepository.setKeyguardShowing(true)
+ fakeKeyguardRepository.setIsEncryptedOrLockdown(false)
+ fakeUserRepository.setSelectedUserInfo(mainUser)
+ fakeKeyguardRepository.setKeyguardShowing(true)
assertThat(isAvailable).isFalse()
}
@Test
fun widget_tutorialCompletedAndWidgetsAvailable_showWidgetContent() =
- testScope.runTest {
+ kosmos.runTest {
// Keyguard showing, and tutorial completed.
- keyguardRepository.setKeyguardShowing(true)
- keyguardRepository.setKeyguardOccluded(false)
- tutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
+ fakeKeyguardRepository.setKeyguardShowing(true)
+ fakeKeyguardRepository.setKeyguardOccluded(false)
+ fakeCommunalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
val userInfos = listOf(MAIN_USER_INFO, USER_INFO_WORK)
- userRepository.setUserInfos(userInfos)
- userTracker.set(userInfos = userInfos, selectedUserIndex = 0)
- runCurrent()
+ fakeUserRepository.setUserInfos(userInfos)
+ fakeUserTracker.set(userInfos = userInfos, selectedUserIndex = 0)
// Widgets available.
- widgetRepository.addWidget(appWidgetId = 1, userId = USER_INFO_WORK.id)
- widgetRepository.addWidget(appWidgetId = 2, userId = MAIN_USER_INFO.id)
- widgetRepository.addWidget(appWidgetId = 3, userId = MAIN_USER_INFO.id)
+ fakeCommunalWidgetRepository.addWidget(appWidgetId = 1, userId = USER_INFO_WORK.id)
+ fakeCommunalWidgetRepository.addWidget(appWidgetId = 2, userId = MAIN_USER_INFO.id)
+ fakeCommunalWidgetRepository.addWidget(appWidgetId = 3, userId = MAIN_USER_INFO.id)
val widgetContent by collectLastValue(underTest.widgetContent)
@@ -356,18 +308,18 @@ class CommunalInteractorTest(flags: FlagsParameterization) : SysuiTestCase() {
totalTargets: Int,
expectedSizes: List<CommunalContentSize>,
) =
- testScope.runTest {
+ kosmos.runTest {
// Keyguard showing, and tutorial completed.
- keyguardRepository.setKeyguardShowing(true)
- keyguardRepository.setKeyguardOccluded(false)
- tutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
+ fakeKeyguardRepository.setKeyguardShowing(true)
+ fakeKeyguardRepository.setKeyguardOccluded(false)
+ fakeCommunalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
val targets = mutableListOf<CommunalSmartspaceTimer>()
for (index in 0 until totalTargets) {
targets.add(smartspaceTimer(index.toString()))
}
- smartspaceRepository.setTimers(targets)
+ fakeCommunalSmartspaceRepository.setTimers(targets)
val smartspaceContent by collectLastValue(underTest.ongoingContent(false))
assertThat(smartspaceContent?.size).isEqualTo(totalTargets)
@@ -378,12 +330,12 @@ class CommunalInteractorTest(flags: FlagsParameterization) : SysuiTestCase() {
@Test
fun umo_mediaPlaying_showsUmo() =
- testScope.runTest {
+ kosmos.runTest {
// Tutorial completed.
- tutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
+ fakeCommunalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
// Media is playing.
- mediaRepository.mediaActive()
+ fakeCommunalMediaRepository.mediaActive()
val umoContent by collectLastValue(underTest.ongoingContent(true))
@@ -394,12 +346,12 @@ class CommunalInteractorTest(flags: FlagsParameterization) : SysuiTestCase() {
@Test
fun umo_mediaPlaying_mediaHostNotVisible_hidesUmo() =
- testScope.runTest {
+ kosmos.runTest {
// Tutorial completed.
- tutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
+ fakeCommunalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
// Media is playing.
- mediaRepository.mediaActive()
+ fakeCommunalMediaRepository.mediaActive()
val umoContent by collectLastValue(underTest.ongoingContent(false))
assertThat(umoContent?.size).isEqualTo(0)
@@ -409,26 +361,26 @@ class CommunalInteractorTest(flags: FlagsParameterization) : SysuiTestCase() {
@Test
@DisableFlags(FLAG_COMMUNAL_RESPONSIVE_GRID)
fun ongoing_shouldOrderAndSizeByTimestamp() =
- testScope.runTest {
+ kosmos.runTest {
// Keyguard showing, and tutorial completed.
- keyguardRepository.setKeyguardShowing(true)
- keyguardRepository.setKeyguardOccluded(false)
- tutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
+ fakeKeyguardRepository.setKeyguardShowing(true)
+ fakeKeyguardRepository.setKeyguardOccluded(false)
+ fakeCommunalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
// Timer1 started
val timer1 = smartspaceTimer("timer1", timestamp = 1L)
- smartspaceRepository.setTimers(listOf(timer1))
+ fakeCommunalSmartspaceRepository.setTimers(listOf(timer1))
// Umo started
- mediaRepository.mediaActive(timestamp = 2L)
+ fakeCommunalMediaRepository.mediaActive(timestamp = 2L)
// Timer2 started
val timer2 = smartspaceTimer("timer2", timestamp = 3L)
- smartspaceRepository.setTimers(listOf(timer1, timer2))
+ fakeCommunalSmartspaceRepository.setTimers(listOf(timer1, timer2))
// Timer3 started
val timer3 = smartspaceTimer("timer3", timestamp = 4L)
- smartspaceRepository.setTimers(listOf(timer1, timer2, timer3))
+ fakeCommunalSmartspaceRepository.setTimers(listOf(timer1, timer2, timer3))
val ongoingContent by collectLastValue(underTest.ongoingContent(true))
assertThat(ongoingContent?.size).isEqualTo(4)
@@ -447,8 +399,8 @@ class CommunalInteractorTest(flags: FlagsParameterization) : SysuiTestCase() {
@Test
fun ctaTile_showsByDefault() =
- testScope.runTest {
- tutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
+ kosmos.runTest {
+ fakeCommunalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
val ctaTileContent by collectLastValue(underTest.ctaTileContent)
@@ -461,14 +413,13 @@ class CommunalInteractorTest(flags: FlagsParameterization) : SysuiTestCase() {
@Test
fun ctaTile_afterDismiss_doesNotShow() =
- testScope.runTest {
+ kosmos.runTest {
// Set to main user, so we can dismiss the tile for the main user.
- val user = userRepository.asMainUser()
- userTracker.set(userInfos = listOf(user), selectedUserIndex = 0)
- runCurrent()
+ val user = fakeUserRepository.asMainUser()
+ fakeUserTracker.set(userInfos = listOf(user), selectedUserIndex = 0)
- tutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
- communalPrefsRepository.setCtaDismissed(user)
+ fakeCommunalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
+ fakeCommunalPrefsRepository.setCtaDismissed(user)
val ctaTileContent by collectLastValue(underTest.ctaTileContent)
@@ -477,36 +428,30 @@ class CommunalInteractorTest(flags: FlagsParameterization) : SysuiTestCase() {
@Test
fun listensToSceneChange() =
- testScope.runTest {
+ kosmos.runTest {
kosmos.setCommunalAvailable(true)
- runCurrent()
- var desiredScene = collectLastValue(underTest.desiredScene)
- runCurrent()
- assertThat(desiredScene()).isEqualTo(CommunalScenes.Blank)
+ val desiredScene by collectLastValue(underTest.desiredScene)
+ assertThat(desiredScene).isEqualTo(CommunalScenes.Blank)
val targetScene = CommunalScenes.Communal
- communalRepository.changeScene(targetScene)
- desiredScene = collectLastValue(underTest.desiredScene)
- runCurrent()
- assertThat(desiredScene()).isEqualTo(targetScene)
+ fakeCommunalSceneRepository.changeScene(targetScene)
+ assertThat(desiredScene).isEqualTo(targetScene)
}
@Test
fun updatesScene() =
- testScope.runTest {
+ kosmos.runTest {
val targetScene = CommunalScenes.Communal
-
underTest.changeScene(targetScene, "test")
- val desiredScene = collectLastValue(communalRepository.currentScene)
- runCurrent()
- assertThat(desiredScene()).isEqualTo(targetScene)
+ val desiredScene by collectLastValue(fakeCommunalSceneRepository.currentScene)
+ assertThat(desiredScene).isEqualTo(targetScene)
}
@Test
fun transitionProgress_onTargetScene_fullProgress() =
- testScope.runTest {
+ kosmos.runTest {
val targetScene = CommunalScenes.Blank
val transitionProgressFlow = underTest.transitionProgressToScene(targetScene)
val transitionProgress by collectLastValue(transitionProgressFlow)
@@ -524,7 +469,7 @@ class CommunalInteractorTest(flags: FlagsParameterization) : SysuiTestCase() {
@Test
fun transitionProgress_notOnTargetScene_noProgress() =
- testScope.runTest {
+ kosmos.runTest {
val targetScene = CommunalScenes.Blank
val currentScene = CommunalScenes.Communal
val transitionProgressFlow = underTest.transitionProgressToScene(targetScene)
@@ -543,7 +488,7 @@ class CommunalInteractorTest(flags: FlagsParameterization) : SysuiTestCase() {
@Test
fun transitionProgress_transitioningToTrackedScene() =
- testScope.runTest {
+ kosmos.runTest {
val currentScene = CommunalScenes.Communal
val targetScene = CommunalScenes.Blank
val transitionProgressFlow = underTest.transitionProgressToScene(targetScene)
@@ -591,7 +536,7 @@ class CommunalInteractorTest(flags: FlagsParameterization) : SysuiTestCase() {
@Test
fun transitionProgress_transitioningAwayFromTrackedScene() =
- testScope.runTest {
+ kosmos.runTest {
val currentScene = CommunalScenes.Blank
val targetScene = CommunalScenes.Communal
val transitionProgressFlow = underTest.transitionProgressToScene(currentScene)
@@ -642,52 +587,42 @@ class CommunalInteractorTest(flags: FlagsParameterization) : SysuiTestCase() {
@Test
fun isCommunalShowing() =
- testScope.runTest {
+ kosmos.runTest {
kosmos.setCommunalAvailable(true)
- runCurrent()
- var isCommunalShowing = collectLastValue(underTest.isCommunalShowing)
- runCurrent()
- assertThat(isCommunalShowing()).isEqualTo(false)
+ val isCommunalShowing by collectLastValue(underTest.isCommunalShowing)
+ assertThat(isCommunalShowing).isEqualTo(false)
underTest.changeScene(CommunalScenes.Communal, "test")
-
- isCommunalShowing = collectLastValue(underTest.isCommunalShowing)
- runCurrent()
- assertThat(isCommunalShowing()).isEqualTo(true)
+ assertThat(isCommunalShowing).isEqualTo(true)
}
@Test
fun isCommunalShowing_whenSceneContainerDisabled() =
- testScope.runTest {
+ kosmos.runTest {
kosmos.setCommunalAvailable(true)
- runCurrent()
// Verify default is false
val isCommunalShowing by collectLastValue(underTest.isCommunalShowing)
- runCurrent()
assertThat(isCommunalShowing).isFalse()
// Verify scene changes with the flag doesn't have any impact
sceneInteractor.changeScene(Scenes.Communal, loggingReason = "")
- runCurrent()
assertThat(isCommunalShowing).isFalse()
// Verify scene changes (without the flag) to communal sets the value to true
underTest.changeScene(CommunalScenes.Communal, "test")
- runCurrent()
assertThat(isCommunalShowing).isTrue()
// Verify scene changes (without the flag) to blank sets the value back to false
underTest.changeScene(CommunalScenes.Blank, "test")
- runCurrent()
assertThat(isCommunalShowing).isFalse()
}
@Test
@EnableSceneContainer
fun isCommunalShowing_whenSceneContainerEnabled() =
- testScope.runTest {
+ kosmos.runTest {
// Verify default is false
val isCommunalShowing by collectLastValue(underTest.isCommunalShowing)
assertThat(isCommunalShowing).isFalse()
@@ -704,7 +639,7 @@ class CommunalInteractorTest(flags: FlagsParameterization) : SysuiTestCase() {
@Test
@EnableSceneContainer
fun isCommunalShowing_whenSceneContainerEnabledAndChangeToLegacyScene() =
- testScope.runTest {
+ kosmos.runTest {
// Verify default is false
val isCommunalShowing by collectLastValue(underTest.isCommunalShowing)
assertThat(isCommunalShowing).isFalse()
@@ -720,21 +655,19 @@ class CommunalInteractorTest(flags: FlagsParameterization) : SysuiTestCase() {
@Test
fun isIdleOnCommunal() =
- testScope.runTest {
+ kosmos.runTest {
val transitionState =
MutableStateFlow<ObservableTransitionState>(
ObservableTransitionState.Idle(CommunalScenes.Blank)
)
- communalRepository.setTransitionState(transitionState)
+ fakeCommunalSceneRepository.setTransitionState(transitionState)
// isIdleOnCommunal is false when not on communal.
val isIdleOnCommunal by collectLastValue(underTest.isIdleOnCommunal)
- runCurrent()
assertThat(isIdleOnCommunal).isEqualTo(false)
// Transition to communal.
transitionState.value = ObservableTransitionState.Idle(CommunalScenes.Communal)
- runCurrent()
// isIdleOnCommunal is now true since we're on communal.
assertThat(isIdleOnCommunal).isEqualTo(true)
@@ -749,7 +682,6 @@ class CommunalInteractorTest(flags: FlagsParameterization) : SysuiTestCase() {
isInitiatedByUserInput = false,
isUserInputOngoing = flowOf(false),
)
- runCurrent()
// isIdleOnCommunal turns false as soon as transition away starts.
assertThat(isIdleOnCommunal).isEqualTo(false)
@@ -757,12 +689,12 @@ class CommunalInteractorTest(flags: FlagsParameterization) : SysuiTestCase() {
@Test
fun isCommunalVisible() =
- testScope.runTest {
+ kosmos.runTest {
val transitionState =
MutableStateFlow<ObservableTransitionState>(
ObservableTransitionState.Idle(CommunalScenes.Blank)
)
- communalRepository.setTransitionState(transitionState)
+ fakeCommunalSceneRepository.setTransitionState(transitionState)
// isCommunalVisible is false when not on communal.
val isCommunalVisible by collectLastValue(underTest.isCommunalVisible)
@@ -805,7 +737,7 @@ class CommunalInteractorTest(flags: FlagsParameterization) : SysuiTestCase() {
@Test
fun testShowWidgetEditorStartsActivity() =
- testScope.runTest {
+ kosmos.runTest {
val editModeState by collectLastValue(communalSceneInteractor.editModeState)
underTest.showWidgetEditor()
@@ -816,14 +748,14 @@ class CommunalInteractorTest(flags: FlagsParameterization) : SysuiTestCase() {
@Test
fun showWidgetEditor_openWidgetPickerOnStart_startsActivity() =
- testScope.runTest {
+ kosmos.runTest {
underTest.showWidgetEditor(shouldOpenWidgetPickerOnStart = true)
verify(editWidgetsActivityStarter).startActivity(shouldOpenWidgetPickerOnStart = true)
}
@Test
fun navigateToCommunalWidgetSettings_startsActivity() =
- testScope.runTest {
+ kosmos.runTest {
underTest.navigateToCommunalWidgetSettings()
val intentCaptor = argumentCaptor<Intent>()
verify(activityStarter)
@@ -833,23 +765,22 @@ class CommunalInteractorTest(flags: FlagsParameterization) : SysuiTestCase() {
@Test
fun filterWidgets_whenUserProfileRemoved() =
- testScope.runTest {
+ kosmos.runTest {
// Keyguard showing, and tutorial completed.
- keyguardRepository.setKeyguardShowing(true)
- keyguardRepository.setKeyguardOccluded(false)
- tutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
+ fakeKeyguardRepository.setKeyguardShowing(true)
+ fakeKeyguardRepository.setKeyguardOccluded(false)
+ fakeCommunalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
// Only main user exists.
val userInfos = listOf(MAIN_USER_INFO)
- userRepository.setUserInfos(userInfos)
- userTracker.set(userInfos = userInfos, selectedUserIndex = 0)
- runCurrent()
+ fakeUserRepository.setUserInfos(userInfos)
+ fakeUserTracker.set(userInfos = userInfos, selectedUserIndex = 0)
val widgetContent by collectLastValue(underTest.widgetContent)
// Given three widgets, and one of them is associated with pre-existing work profile.
- widgetRepository.addWidget(appWidgetId = 1, userId = USER_INFO_WORK.id)
- widgetRepository.addWidget(appWidgetId = 2, userId = MAIN_USER_INFO.id)
- widgetRepository.addWidget(appWidgetId = 3, userId = MAIN_USER_INFO.id)
+ fakeCommunalWidgetRepository.addWidget(appWidgetId = 1, userId = USER_INFO_WORK.id)
+ fakeCommunalWidgetRepository.addWidget(appWidgetId = 2, userId = MAIN_USER_INFO.id)
+ fakeCommunalWidgetRepository.addWidget(appWidgetId = 3, userId = MAIN_USER_INFO.id)
// One widget is filtered out and the remaining two link to main user id.
assertThat(checkNotNull(widgetContent).size).isEqualTo(2)
@@ -867,17 +798,16 @@ class CommunalInteractorTest(flags: FlagsParameterization) : SysuiTestCase() {
@Test
fun widgetContent_inQuietMode() =
- testScope.runTest {
+ kosmos.runTest {
// Keyguard showing, and tutorial completed.
- keyguardRepository.setKeyguardShowing(true)
- keyguardRepository.setKeyguardOccluded(false)
- tutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
+ fakeKeyguardRepository.setKeyguardShowing(true)
+ fakeKeyguardRepository.setKeyguardOccluded(false)
+ fakeCommunalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
// Work profile is set up.
val userInfos = listOf(MAIN_USER_INFO, USER_INFO_WORK)
- userRepository.setUserInfos(userInfos)
- userTracker.set(userInfos = userInfos, selectedUserIndex = 0)
- runCurrent()
+ fakeUserRepository.setUserInfos(userInfos)
+ fakeUserTracker.set(userInfos = userInfos, selectedUserIndex = 0)
// When work profile is paused.
whenever(userManager.isQuietModeEnabled(eq(UserHandle.of(USER_INFO_WORK.id))))
@@ -885,9 +815,9 @@ class CommunalInteractorTest(flags: FlagsParameterization) : SysuiTestCase() {
whenever(userManager.isManagedProfile(eq(USER_INFO_WORK.id))).thenReturn(true)
val widgetContent by collectLastValue(underTest.widgetContent)
- widgetRepository.addWidget(appWidgetId = 1, userId = USER_INFO_WORK.id)
- widgetRepository.addWidget(appWidgetId = 2, userId = MAIN_USER_INFO.id)
- widgetRepository.addWidget(appWidgetId = 3, userId = MAIN_USER_INFO.id)
+ fakeCommunalWidgetRepository.addWidget(appWidgetId = 1, userId = USER_INFO_WORK.id)
+ fakeCommunalWidgetRepository.addWidget(appWidgetId = 2, userId = MAIN_USER_INFO.id)
+ fakeCommunalWidgetRepository.addWidget(appWidgetId = 3, userId = MAIN_USER_INFO.id)
// The work profile widget is in quiet mode, while other widgets are not.
assertThat(widgetContent).hasSize(3)
@@ -911,23 +841,25 @@ class CommunalInteractorTest(flags: FlagsParameterization) : SysuiTestCase() {
@Test
fun filterWidgets_whenDisallowedByDevicePolicyForWorkProfile() =
- testScope.runTest {
+ kosmos.runTest {
// Keyguard showing, and tutorial completed.
- keyguardRepository.setKeyguardShowing(true)
- keyguardRepository.setKeyguardOccluded(false)
- tutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
+ fakeKeyguardRepository.setKeyguardShowing(true)
+ fakeKeyguardRepository.setKeyguardOccluded(false)
+ fakeCommunalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
val userInfos = listOf(MAIN_USER_INFO, USER_INFO_WORK)
- userRepository.setUserInfos(userInfos)
- userTracker.set(userInfos = userInfos, selectedUserIndex = 0)
- userRepository.setSelectedUserInfo(MAIN_USER_INFO)
- runCurrent()
+ fakeUserRepository.setUserInfos(userInfos)
+ fakeUserTracker.set(userInfos = userInfos, selectedUserIndex = 0)
+ fakeUserRepository.setSelectedUserInfo(MAIN_USER_INFO)
val widgetContent by collectLastValue(underTest.widgetContent)
// One available work widget, one pending work widget, and one regular available widget.
- widgetRepository.addWidget(appWidgetId = 1, userId = USER_INFO_WORK.id)
- widgetRepository.addPendingWidget(appWidgetId = 2, userId = USER_INFO_WORK.id)
- widgetRepository.addWidget(appWidgetId = 3, userId = MAIN_USER_INFO.id)
+ fakeCommunalWidgetRepository.addWidget(appWidgetId = 1, userId = USER_INFO_WORK.id)
+ fakeCommunalWidgetRepository.addPendingWidget(
+ appWidgetId = 2,
+ userId = USER_INFO_WORK.id,
+ )
+ fakeCommunalWidgetRepository.addWidget(appWidgetId = 3, userId = MAIN_USER_INFO.id)
setKeyguardFeaturesDisabled(
USER_INFO_WORK,
@@ -941,23 +873,25 @@ class CommunalInteractorTest(flags: FlagsParameterization) : SysuiTestCase() {
@Test
fun filterWidgets_whenAllowedByDevicePolicyForWorkProfile() =
- testScope.runTest {
+ kosmos.runTest {
// Keyguard showing, and tutorial completed.
- keyguardRepository.setKeyguardShowing(true)
- keyguardRepository.setKeyguardOccluded(false)
- tutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
+ fakeKeyguardRepository.setKeyguardShowing(true)
+ fakeKeyguardRepository.setKeyguardOccluded(false)
+ fakeCommunalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
val userInfos = listOf(MAIN_USER_INFO, USER_INFO_WORK)
- userRepository.setUserInfos(userInfos)
- userTracker.set(userInfos = userInfos, selectedUserIndex = 0)
- userRepository.setSelectedUserInfo(MAIN_USER_INFO)
- runCurrent()
+ fakeUserRepository.setUserInfos(userInfos)
+ fakeUserTracker.set(userInfos = userInfos, selectedUserIndex = 0)
+ fakeUserRepository.setSelectedUserInfo(MAIN_USER_INFO)
val widgetContent by collectLastValue(underTest.widgetContent)
// Given three widgets, and one of them is associated with work profile.
- widgetRepository.addWidget(appWidgetId = 1, userId = USER_INFO_WORK.id)
- widgetRepository.addPendingWidget(appWidgetId = 2, userId = USER_INFO_WORK.id)
- widgetRepository.addWidget(appWidgetId = 3, userId = MAIN_USER_INFO.id)
+ fakeCommunalWidgetRepository.addWidget(appWidgetId = 1, userId = USER_INFO_WORK.id)
+ fakeCommunalWidgetRepository.addPendingWidget(
+ appWidgetId = 2,
+ userId = USER_INFO_WORK.id,
+ )
+ fakeCommunalWidgetRepository.addWidget(appWidgetId = 3, userId = MAIN_USER_INFO.id)
setKeyguardFeaturesDisabled(
USER_INFO_WORK,
@@ -973,7 +907,7 @@ class CommunalInteractorTest(flags: FlagsParameterization) : SysuiTestCase() {
@Test
fun showCommunalFromOccluded_enteredOccludedFromHub() =
- testScope.runTest {
+ kosmos.runTest {
kosmos.setCommunalAvailable(true)
val showCommunalFromOccluded by collectLastValue(underTest.showCommunalFromOccluded)
assertThat(showCommunalFromOccluded).isFalse()
@@ -989,7 +923,7 @@ class CommunalInteractorTest(flags: FlagsParameterization) : SysuiTestCase() {
@Test
fun showCommunalFromOccluded_enteredOccludedFromLockscreen() =
- testScope.runTest {
+ kosmos.runTest {
kosmos.setCommunalAvailable(true)
val showCommunalFromOccluded by collectLastValue(underTest.showCommunalFromOccluded)
assertThat(showCommunalFromOccluded).isFalse()
@@ -1005,7 +939,7 @@ class CommunalInteractorTest(flags: FlagsParameterization) : SysuiTestCase() {
@Test
fun showCommunalFromOccluded_communalBecomesUnavailableWhileOccluded() =
- testScope.runTest {
+ kosmos.runTest {
kosmos.setCommunalAvailable(true)
val showCommunalFromOccluded by collectLastValue(underTest.showCommunalFromOccluded)
assertThat(showCommunalFromOccluded).isFalse()
@@ -1015,7 +949,6 @@ class CommunalInteractorTest(flags: FlagsParameterization) : SysuiTestCase() {
to = KeyguardState.OCCLUDED,
testScope,
)
- runCurrent()
kosmos.setCommunalAvailable(false)
assertThat(showCommunalFromOccluded).isFalse()
@@ -1023,7 +956,7 @@ class CommunalInteractorTest(flags: FlagsParameterization) : SysuiTestCase() {
@Test
fun showCommunalFromOccluded_showBouncerWhileOccluded() =
- testScope.runTest {
+ kosmos.runTest {
kosmos.setCommunalAvailable(true)
val showCommunalFromOccluded by collectLastValue(underTest.showCommunalFromOccluded)
assertThat(showCommunalFromOccluded).isFalse()
@@ -1033,7 +966,6 @@ class CommunalInteractorTest(flags: FlagsParameterization) : SysuiTestCase() {
to = KeyguardState.OCCLUDED,
testScope,
)
- runCurrent()
kosmos.fakeKeyguardTransitionRepository.sendTransitionSteps(
from = KeyguardState.OCCLUDED,
to = KeyguardState.PRIMARY_BOUNCER,
@@ -1045,7 +977,7 @@ class CommunalInteractorTest(flags: FlagsParameterization) : SysuiTestCase() {
@Test
fun showCommunalFromOccluded_enteredOccludedFromDreaming() =
- testScope.runTest {
+ kosmos.runTest {
kosmos.setCommunalAvailable(true)
val showCommunalFromOccluded by collectLastValue(underTest.showCommunalFromOccluded)
assertThat(showCommunalFromOccluded).isFalse()
@@ -1069,7 +1001,7 @@ class CommunalInteractorTest(flags: FlagsParameterization) : SysuiTestCase() {
@Test
fun dismissDisclaimerSetsDismissedFlag() =
- testScope.runTest {
+ kosmos.runTest {
val disclaimerDismissed by collectLastValue(underTest.isDisclaimerDismissed)
assertThat(disclaimerDismissed).isFalse()
underTest.setDisclaimerDismissed()
@@ -1078,17 +1010,17 @@ class CommunalInteractorTest(flags: FlagsParameterization) : SysuiTestCase() {
@Test
fun dismissDisclaimerTimeoutResetsDismissedFlag() =
- testScope.runTest {
+ kosmos.runTest {
val disclaimerDismissed by collectLastValue(underTest.isDisclaimerDismissed)
underTest.setDisclaimerDismissed()
assertThat(disclaimerDismissed).isTrue()
- advanceTimeBy(CommunalInteractor.DISCLAIMER_RESET_MILLIS)
+ testScope.advanceTimeBy(CommunalInteractor.DISCLAIMER_RESET_MILLIS)
assertThat(disclaimerDismissed).isFalse()
}
@Test
fun settingSelectedKey_flowUpdated() {
- testScope.runTest {
+ kosmos.runTest {
val key = "test"
val selectedKey by collectLastValue(underTest.selectedKey)
underTest.setSelectedKey(key)
@@ -1098,36 +1030,35 @@ class CommunalInteractorTest(flags: FlagsParameterization) : SysuiTestCase() {
@Test
fun unpauseWorkProfileEnablesWorkMode() =
- testScope.runTest {
+ kosmos.runTest {
underTest.unpauseWorkProfile()
- assertThat(managedProfileController.isWorkModeEnabled()).isTrue()
+ assertThat(fakeManagedProfileController.isWorkModeEnabled()).isTrue()
}
@Test
@EnableFlags(FLAG_COMMUNAL_WIDGET_RESIZING)
@DisableFlags(FLAG_COMMUNAL_RESPONSIVE_GRID)
fun resizeWidget_withoutUpdatingOrder() =
- testScope.runTest {
+ kosmos.runTest {
val userInfos = listOf(MAIN_USER_INFO)
- userRepository.setUserInfos(userInfos)
- userTracker.set(userInfos = userInfos, selectedUserIndex = 0)
- runCurrent()
+ fakeUserRepository.setUserInfos(userInfos)
+ fakeUserTracker.set(userInfos = userInfos, selectedUserIndex = 0)
// Widgets available.
- widgetRepository.addWidget(
+ fakeCommunalWidgetRepository.addWidget(
appWidgetId = 1,
userId = MAIN_USER_INFO.id,
rank = 0,
spanY = CommunalContentSize.FixedSize.HALF.span,
)
- widgetRepository.addWidget(
+ fakeCommunalWidgetRepository.addWidget(
appWidgetId = 2,
userId = MAIN_USER_INFO.id,
rank = 1,
spanY = CommunalContentSize.FixedSize.HALF.span,
)
- widgetRepository.addWidget(
+ fakeCommunalWidgetRepository.addWidget(
appWidgetId = 3,
userId = MAIN_USER_INFO.id,
rank = 2,
@@ -1159,26 +1090,25 @@ class CommunalInteractorTest(flags: FlagsParameterization) : SysuiTestCase() {
@Test
@EnableFlags(FLAG_COMMUNAL_WIDGET_RESIZING, FLAG_COMMUNAL_RESPONSIVE_GRID)
fun resizeWidget_withoutUpdatingOrder_responsive() =
- testScope.runTest {
+ kosmos.runTest {
val userInfos = listOf(MAIN_USER_INFO)
- userRepository.setUserInfos(userInfos)
- userTracker.set(userInfos = userInfos, selectedUserIndex = 0)
- runCurrent()
+ fakeUserRepository.setUserInfos(userInfos)
+ fakeUserTracker.set(userInfos = userInfos, selectedUserIndex = 0)
// Widgets available.
- widgetRepository.addWidget(
+ fakeCommunalWidgetRepository.addWidget(
appWidgetId = 1,
userId = MAIN_USER_INFO.id,
rank = 0,
spanY = 1,
)
- widgetRepository.addWidget(
+ fakeCommunalWidgetRepository.addWidget(
appWidgetId = 2,
userId = MAIN_USER_INFO.id,
rank = 1,
spanY = 1,
)
- widgetRepository.addWidget(
+ fakeCommunalWidgetRepository.addWidget(
appWidgetId = 3,
userId = MAIN_USER_INFO.id,
rank = 2,
@@ -1211,26 +1141,25 @@ class CommunalInteractorTest(flags: FlagsParameterization) : SysuiTestCase() {
@EnableFlags(FLAG_COMMUNAL_WIDGET_RESIZING)
@DisableFlags(FLAG_COMMUNAL_RESPONSIVE_GRID)
fun resizeWidget_andUpdateOrder() =
- testScope.runTest {
+ kosmos.runTest {
val userInfos = listOf(MAIN_USER_INFO)
- userRepository.setUserInfos(userInfos)
- userTracker.set(userInfos = userInfos, selectedUserIndex = 0)
- runCurrent()
+ fakeUserRepository.setUserInfos(userInfos)
+ fakeUserTracker.set(userInfos = userInfos, selectedUserIndex = 0)
// Widgets available.
- widgetRepository.addWidget(
+ fakeCommunalWidgetRepository.addWidget(
appWidgetId = 1,
userId = MAIN_USER_INFO.id,
rank = 0,
spanY = CommunalContentSize.FixedSize.HALF.span,
)
- widgetRepository.addWidget(
+ fakeCommunalWidgetRepository.addWidget(
appWidgetId = 2,
userId = MAIN_USER_INFO.id,
rank = 1,
spanY = CommunalContentSize.FixedSize.HALF.span,
)
- widgetRepository.addWidget(
+ fakeCommunalWidgetRepository.addWidget(
appWidgetId = 3,
userId = MAIN_USER_INFO.id,
rank = 2,
@@ -1266,26 +1195,25 @@ class CommunalInteractorTest(flags: FlagsParameterization) : SysuiTestCase() {
@Test
@EnableFlags(FLAG_COMMUNAL_WIDGET_RESIZING, FLAG_COMMUNAL_RESPONSIVE_GRID)
fun resizeWidget_andUpdateOrder_responsive() =
- testScope.runTest {
+ kosmos.runTest {
val userInfos = listOf(MAIN_USER_INFO)
- userRepository.setUserInfos(userInfos)
- userTracker.set(userInfos = userInfos, selectedUserIndex = 0)
- runCurrent()
+ fakeUserRepository.setUserInfos(userInfos)
+ fakeUserTracker.set(userInfos = userInfos, selectedUserIndex = 0)
// Widgets available.
- widgetRepository.addWidget(
+ fakeCommunalWidgetRepository.addWidget(
appWidgetId = 1,
userId = MAIN_USER_INFO.id,
rank = 0,
spanY = 1,
)
- widgetRepository.addWidget(
+ fakeCommunalWidgetRepository.addWidget(
appWidgetId = 2,
userId = MAIN_USER_INFO.id,
rank = 1,
spanY = 1,
)
- widgetRepository.addWidget(
+ fakeCommunalWidgetRepository.addWidget(
appWidgetId = 3,
userId = MAIN_USER_INFO.id,
rank = 2,
@@ -1318,6 +1246,66 @@ class CommunalInteractorTest(flags: FlagsParameterization) : SysuiTestCase() {
.inOrder()
}
+ @Test
+ fun showCommunalWhileCharging() =
+ kosmos.runTest {
+ fakeKeyguardRepository.setIsEncryptedOrLockdown(false)
+ fakeUserRepository.setSelectedUserInfo(mainUser)
+ fakeKeyguardRepository.setKeyguardShowing(true)
+ fakeSettings.putIntForUser(
+ Settings.Secure.SCREENSAVER_ACTIVATE_ON_SLEEP,
+ 1,
+ mainUser.id,
+ )
+
+ val shouldShowCommunal by collectLastValue(underTest.shouldShowCommunal)
+ batteryRepository.fake.setDevicePluggedIn(false)
+ assertThat(shouldShowCommunal).isFalse()
+
+ batteryRepository.fake.setDevicePluggedIn(true)
+ assertThat(shouldShowCommunal).isTrue()
+ }
+
+ @Test
+ fun showCommunalWhilePosturedAndCharging() =
+ kosmos.runTest {
+ fakeKeyguardRepository.setIsEncryptedOrLockdown(false)
+ fakeUserRepository.setSelectedUserInfo(mainUser)
+ fakeKeyguardRepository.setKeyguardShowing(true)
+ fakeSettings.putIntForUser(
+ Settings.Secure.SCREENSAVER_ACTIVATE_ON_POSTURED,
+ 1,
+ mainUser.id,
+ )
+
+ val shouldShowCommunal by collectLastValue(underTest.shouldShowCommunal)
+ batteryRepository.fake.setDevicePluggedIn(true)
+ posturingRepository.fake.setPosturedState(PosturedState.NotPostured)
+ assertThat(shouldShowCommunal).isFalse()
+
+ posturingRepository.fake.setPosturedState(PosturedState.Postured(1f))
+ assertThat(shouldShowCommunal).isTrue()
+ }
+
+ @Test
+ fun showCommunalWhileDocked() =
+ kosmos.runTest {
+ fakeKeyguardRepository.setIsEncryptedOrLockdown(false)
+ fakeUserRepository.setSelectedUserInfo(mainUser)
+ fakeKeyguardRepository.setKeyguardShowing(true)
+ fakeSettings.putIntForUser(Settings.Secure.SCREENSAVER_ACTIVATE_ON_DOCK, 1, mainUser.id)
+
+ batteryRepository.fake.setDevicePluggedIn(true)
+ fakeDockManager.setIsDocked(false)
+
+ val shouldShowCommunal by collectLastValue(underTest.shouldShowCommunal)
+ assertThat(shouldShowCommunal).isFalse()
+
+ fakeDockManager.setIsDocked(true)
+ fakeDockManager.setDockEvent(DockManager.STATE_DOCKED)
+ assertThat(shouldShowCommunal).isTrue()
+ }
+
private fun setKeyguardFeaturesDisabled(user: UserInfo, disabledFlags: Int) {
whenever(kosmos.devicePolicyManager.getKeyguardDisabledFeatures(nullable(), eq(user.id)))
.thenReturn(disabledFlags)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalPrefsInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalPrefsInteractorTest.kt
index 1fef6932ecca..1f5f8cedab02 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalPrefsInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalPrefsInteractorTest.kt
@@ -108,6 +108,43 @@ class CommunalPrefsInteractorTest : SysuiTestCase() {
assertThat(isHubOnboardingDismissed).isFalse()
}
+ @Test
+ fun setDreamButtonTooltipDismissed_currentUser() =
+ testScope.runTest {
+ setSelectedUser(MAIN_USER)
+ val isDreamButtonTooltipDismissed by
+ collectLastValue(underTest.isDreamButtonTooltipDismissed)
+
+ assertThat(isDreamButtonTooltipDismissed).isFalse()
+ underTest.setDreamButtonTooltipDismissed(MAIN_USER)
+ assertThat(isDreamButtonTooltipDismissed).isTrue()
+ }
+
+ @Test
+ fun setDreamButtonTooltipDismissed_anotherUser() =
+ testScope.runTest {
+ setSelectedUser(MAIN_USER)
+ val isDreamButtonTooltipDismissed by
+ collectLastValue(underTest.isDreamButtonTooltipDismissed)
+
+ assertThat(isDreamButtonTooltipDismissed).isFalse()
+ underTest.setDreamButtonTooltipDismissed(SECONDARY_USER)
+ assertThat(isDreamButtonTooltipDismissed).isFalse()
+ }
+
+ @Test
+ fun isDreamButtonTooltipDismissed_userSwitch() =
+ testScope.runTest {
+ setSelectedUser(MAIN_USER)
+ underTest.setDreamButtonTooltipDismissed(MAIN_USER)
+ val isDreamButtonTooltipDismissed by
+ collectLastValue(underTest.isDreamButtonTooltipDismissed)
+
+ assertThat(isDreamButtonTooltipDismissed).isTrue()
+ setSelectedUser(SECONDARY_USER)
+ assertThat(isDreamButtonTooltipDismissed).isFalse()
+ }
+
private suspend fun setSelectedUser(user: UserInfo) {
with(kosmos.fakeUserRepository) {
setUserInfos(listOf(user))
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalSettingsInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalSettingsInteractorTest.kt
index e4916b1a7e46..310bf6486413 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalSettingsInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalSettingsInteractorTest.kt
@@ -21,19 +21,17 @@ import android.app.admin.devicePolicyManager
import android.content.Intent
import android.content.pm.UserInfo
import android.os.UserManager
-import android.os.userManager
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.broadcast.broadcastDispatcher
-import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.kosmos.testScope
-import com.android.systemui.settings.FakeUserTracker
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.collectLastValue
+import com.android.systemui.kosmos.runTest
+import com.android.systemui.kosmos.useUnconfinedTestDispatcher
import com.android.systemui.settings.fakeUserTracker
import com.android.systemui.testKosmos
-import com.android.systemui.user.data.repository.FakeUserRepository
import com.android.systemui.user.data.repository.fakeUserRepository
-import kotlinx.coroutines.test.runTest
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNotNull
import org.junit.Assert.assertNull
@@ -48,34 +46,20 @@ import org.mockito.kotlin.whenever
@RunWith(AndroidJUnit4::class)
class CommunalSettingsInteractorTest : SysuiTestCase() {
- private lateinit var userManager: UserManager
- private lateinit var userRepository: FakeUserRepository
- private lateinit var userTracker: FakeUserTracker
+ private val kosmos = testKosmos().useUnconfinedTestDispatcher()
- private val kosmos = testKosmos()
- private val testScope = kosmos.testScope
-
- private lateinit var underTest: CommunalSettingsInteractor
+ private val Kosmos.underTest by Kosmos.Fixture { communalSettingsInteractor }
@Before
fun setUp() {
- userManager = kosmos.userManager
- userRepository = kosmos.fakeUserRepository
- userTracker = kosmos.fakeUserTracker
-
val userInfos = listOf(MAIN_USER_INFO, USER_INFO_WORK)
- userRepository.setUserInfos(userInfos)
- userTracker.set(
- userInfos = userInfos,
- selectedUserIndex = 0,
- )
-
- underTest = kosmos.communalSettingsInteractor
+ kosmos.fakeUserRepository.setUserInfos(userInfos)
+ kosmos.fakeUserTracker.set(userInfos = userInfos, selectedUserIndex = 0)
}
@Test
fun filterUsers_dontFilteredUsersWhenAllAreAllowed() =
- testScope.runTest {
+ kosmos.runTest {
// If no users have any keyguard features disabled...
val disallowedUser by
collectLastValue(underTest.workProfileUserDisallowedByDevicePolicy)
@@ -85,11 +69,11 @@ class CommunalSettingsInteractorTest : SysuiTestCase() {
@Test
fun filterUsers_filterWorkProfileUserWhenDisallowed() =
- testScope.runTest {
+ kosmos.runTest {
// If the work profile user has keyguard widgets disabled...
setKeyguardFeaturesDisabled(
USER_INFO_WORK,
- DevicePolicyManager.KEYGUARD_DISABLE_WIDGETS_ALL
+ DevicePolicyManager.KEYGUARD_DISABLE_WIDGETS_ALL,
)
// ...then the disallowed user match the work profile
val disallowedUser by
@@ -102,7 +86,7 @@ class CommunalSettingsInteractorTest : SysuiTestCase() {
whenever(
kosmos.devicePolicyManager.getKeyguardDisabledFeatures(
anyOrNull(),
- ArgumentMatchers.eq(user.id)
+ ArgumentMatchers.eq(user.id),
)
)
.thenReturn(disabledFlags)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/viewmodel/CommunalToDreamButtonViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/viewmodel/CommunalToDreamButtonViewModelTest.kt
index 012ae8f12d4a..b747705fa3a2 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/viewmodel/CommunalToDreamButtonViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/viewmodel/CommunalToDreamButtonViewModelTest.kt
@@ -16,6 +16,7 @@
package com.android.systemui.communal.ui.viewmodel
+import android.content.pm.UserInfo
import android.platform.test.annotations.EnableFlags
import android.provider.Settings
import android.service.dream.dreamManager
@@ -24,15 +25,17 @@ import androidx.test.filters.SmallTest
import com.android.internal.logging.uiEventLoggerFake
import com.android.systemui.Flags.FLAG_GLANCEABLE_HUB_V2
import com.android.systemui.SysuiTestCase
+import com.android.systemui.communal.data.repository.fakeCommunalPrefsRepository
+import com.android.systemui.communal.domain.interactor.HubOnboardingInteractorTest.Companion.MAIN_USER
import com.android.systemui.communal.shared.log.CommunalUiEvent
import com.android.systemui.flags.Flags.COMMUNAL_SERVICE_ENABLED
import com.android.systemui.flags.fakeFeatureFlagsClassic
-import com.android.systemui.kosmos.collectLastValue
import com.android.systemui.kosmos.runCurrent
import com.android.systemui.kosmos.runTest
import com.android.systemui.kosmos.testScope
import com.android.systemui.lifecycle.activateIn
import com.android.systemui.plugins.activityStarter
+import com.android.systemui.settings.fakeUserTracker
import com.android.systemui.statusbar.policy.batteryController
import com.android.systemui.testKosmos
import com.android.systemui.user.data.repository.fakeUserRepository
@@ -52,7 +55,6 @@ import org.mockito.kotlin.whenever
class CommunalToDreamButtonViewModelTest : SysuiTestCase() {
private val kosmos = testKosmos()
private val testScope = kosmos.testScope
- private val uiEventLoggerFake = kosmos.uiEventLoggerFake
private val underTest: CommunalToDreamButtonViewModel by lazy {
kosmos.communalToDreamButtonViewModel
}
@@ -68,9 +70,9 @@ class CommunalToDreamButtonViewModelTest : SysuiTestCase() {
with(kosmos) {
runTest {
whenever(batteryController.isPluggedIn()).thenReturn(true)
+ runCurrent()
- val shouldShowButton by collectLastValue(underTest.shouldShowDreamButtonOnHub)
- assertThat(shouldShowButton).isTrue()
+ assertThat(underTest.shouldShowDreamButtonOnHub).isTrue()
}
}
@@ -79,9 +81,9 @@ class CommunalToDreamButtonViewModelTest : SysuiTestCase() {
with(kosmos) {
runTest {
whenever(batteryController.isPluggedIn()).thenReturn(false)
+ runCurrent()
- val shouldShowButton by collectLastValue(underTest.shouldShowDreamButtonOnHub)
- assertThat(shouldShowButton).isFalse()
+ assertThat(underTest.shouldShowDreamButtonOnHub).isFalse()
}
}
@@ -124,6 +126,23 @@ class CommunalToDreamButtonViewModelTest : SysuiTestCase() {
}
@Test
+ fun shouldShowDreamButtonTooltip_trueWhenNotDismissed() =
+ kosmos.runTest {
+ runCurrent()
+ assertThat(underTest.shouldShowTooltip).isTrue()
+ }
+
+ @Test
+ fun shouldShowDreamButtonTooltip_falseWhenDismissed() =
+ kosmos.runTest {
+ setSelectedUser(MAIN_USER)
+ fakeCommunalPrefsRepository.setDreamButtonTooltipDismissed(MAIN_USER)
+ runCurrent()
+
+ assertThat(underTest.shouldShowTooltip).isFalse()
+ }
+
+ @Test
fun onShowDreamButtonTap_eventLogged() =
with(kosmos) {
runTest {
@@ -134,4 +153,12 @@ class CommunalToDreamButtonViewModelTest : SysuiTestCase() {
.isEqualTo(CommunalUiEvent.COMMUNAL_HUB_SHOW_DREAM_BUTTON_TAP.id)
}
}
+
+ private suspend fun setSelectedUser(user: UserInfo) {
+ with(kosmos.fakeUserRepository) {
+ setUserInfos(listOf(user))
+ setSelectedUserInfo(user)
+ }
+ kosmos.fakeUserTracker.set(userInfos = listOf(user), selectedUserIndex = 0)
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryHapticsInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryHapticsInteractorTest.kt
index 6c955bf1818d..5fd480f90ac9 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryHapticsInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryHapticsInteractorTest.kt
@@ -176,14 +176,14 @@ class DeviceEntryHapticsInteractorTest : SysuiTestCase() {
}
@Test
- fun nonPowerButtonFPS_coExFaceFailure_doNotVibrateError() =
+ fun nonPowerButtonFPS_coExFaceFailure_vibrateError() =
testScope.runTest {
val playErrorHaptic by collectLastValue(underTest.playErrorHaptic)
enrollFingerprint(FingerprintSensorType.UDFPS_ULTRASONIC)
enrollFace()
runCurrent()
faceFailure()
- assertThat(playErrorHaptic).isNull()
+ assertThat(playErrorHaptic).isNotNull()
}
@Test
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 ef70305a3f47..af30e435da73 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
@@ -1149,7 +1149,7 @@ class SceneContainerStartableTest : SysuiTestCase() {
@Test
@DisableFlags(Flags.FLAG_MSDL_FEEDBACK)
- fun skipsFaceErrorHaptics_nonSfps_coEx() =
+ fun playsFaceErrorHaptics_nonSfps_coEx() =
testScope.runTest {
val currentSceneKey by collectLastValue(sceneInteractor.currentScene)
val playErrorHaptic by collectLastValue(deviceEntryHapticsInteractor.playErrorHaptic)
@@ -1161,14 +1161,15 @@ class SceneContainerStartableTest : SysuiTestCase() {
underTest.start()
updateFaceAuthStatus(isSuccess = false)
- assertThat(playErrorHaptic).isNull()
- verify(vibratorHelper, never()).vibrateAuthError(anyString())
+ assertThat(playErrorHaptic).isNotNull()
+ assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen)
+ verify(vibratorHelper).vibrateAuthError(anyString())
verify(vibratorHelper, never()).vibrateAuthSuccess(anyString())
}
@Test
@EnableFlags(Flags.FLAG_MSDL_FEEDBACK)
- fun skipsMSDLFaceErrorHaptics_nonSfps_coEx() =
+ fun playsMSDLFaceErrorHaptics_nonSfps_coEx() =
testScope.runTest {
val currentSceneKey by collectLastValue(sceneInteractor.currentScene)
val playErrorHaptic by collectLastValue(deviceEntryHapticsInteractor.playErrorHaptic)
@@ -1180,9 +1181,10 @@ class SceneContainerStartableTest : SysuiTestCase() {
underTest.start()
updateFaceAuthStatus(isSuccess = false)
- assertThat(playErrorHaptic).isNull()
- assertThat(msdlPlayer.latestTokenPlayed).isNull()
- assertThat(msdlPlayer.latestPropertiesPlayed).isNull()
+ assertThat(playErrorHaptic).isNotNull()
+ assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen)
+ assertThat(msdlPlayer.latestTokenPlayed).isEqualTo(MSDLToken.FAILURE)
+ assertThat(msdlPlayer.latestPropertiesPlayed).isEqualTo(authInteractionProperties)
}
@Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.kt
index 39c42f183481..28b2ee8dde06 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.kt
@@ -269,6 +269,36 @@ class NotificationGutsManagerTest(flags: FlagsParameterization) : SysuiTestCase(
}
@Test
+ fun testOpenAndCloseGutsWithoutSave() {
+ val guts = spy(NotificationGuts(mContext))
+ whenever(guts.post(any())).thenAnswer { invocation: InvocationOnMock ->
+ handler.post(((invocation.arguments[0] as Runnable)))
+ null
+ }
+
+ // Test doesn't support animation since the guts view is not attached.
+ doNothing().whenever(guts).openControls(anyInt(), anyInt(), anyBoolean(), any())
+
+ val realRow = createTestNotificationRow()
+ val menuItem = createTestMenuItem(realRow)
+
+ val row = spy(realRow)
+ whenever(row.windowToken).thenReturn(Binder())
+ whenever(row.guts).thenReturn(guts)
+
+ assertTrue(gutsManager.openGutsInternal(row, 0, 0, menuItem))
+ executor.runAllReady()
+ verify(guts).openControls(anyInt(), anyInt(), anyBoolean(), any<Runnable>())
+
+ gutsManager.closeAndUndoGuts()
+
+ verify(guts).closeControls(anyInt(), anyInt(), eq(false), eq(false))
+ verify(row, times(1)).setGutsView(any<MenuItem>())
+ executor.runAllReady()
+ verify(headsUpManager).setGutsShown(realRow.entry, false)
+ }
+
+ @Test
fun testLockscreenShadeVisible_visible_gutsNotClosed() =
testScope.runTest {
// First, start out lockscreen or shade as not visible
@@ -377,52 +407,6 @@ class NotificationGutsManagerTest(flags: FlagsParameterization) : SysuiTestCase(
}
@Test
- fun testChangeDensityOrFontScale() {
- val guts = spy(NotificationGuts(mContext))
- whenever(guts.post(any())).thenAnswer { invocation: InvocationOnMock ->
- handler.post(((invocation.arguments[0] as Runnable)))
- null
- }
-
- // Test doesn't support animation since the guts view is not attached.
- doNothing().whenever(guts).openControls(anyInt(), anyInt(), anyBoolean(), any<Runnable>())
-
- val realRow = createTestNotificationRow()
- val menuItem = createTestMenuItem(realRow)
-
- val row = spy(realRow)
-
- whenever(row.windowToken).thenReturn(Binder())
- whenever(row.guts).thenReturn(guts)
- doNothing().whenever(row).ensureGutsInflated()
-
- val realEntry = realRow.entry
- val entry = spy(realEntry)
-
- whenever(entry.row).thenReturn(row)
- whenever(entry.guts).thenReturn(guts)
-
- assertTrue(gutsManager.openGutsInternal(row, 0, 0, menuItem))
- executor.runAllReady()
- verify(guts).openControls(anyInt(), anyInt(), anyBoolean(), any<Runnable>())
-
- // called once by mGutsManager.bindGuts() in mGutsManager.openGuts()
- verify(row).setGutsView(any<MenuItem>())
-
- row.onDensityOrFontScaleChanged()
- gutsManager.onDensityOrFontScaleChanged(entry)
-
- executor.runAllReady()
-
- gutsManager.closeAndSaveGuts(false, false, false, 0, 0, false)
-
- verify(guts).closeControls(anyBoolean(), anyBoolean(), anyInt(), anyInt(), anyBoolean())
-
- // called again by mGutsManager.bindGuts(), in mGutsManager.onDensityOrFontScaleChanged()
- verify(row, times(2)).setGutsView(any<MenuItem>())
- }
-
- @Test
fun testAppOpsSettingsIntent_camera() {
val row = createTestNotificationRow()
val ops = ArraySet<Int>()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationSnoozeTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationSnoozeTest.java
index 6435e8203d3d..af67a04d2f2a 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationSnoozeTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationSnoozeTest.java
@@ -16,29 +16,44 @@
package com.android.systemui.statusbar.notification.row;
+import static com.google.common.truth.Truth.assertThat;
+
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertNotNull;
import static junit.framework.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import android.platform.test.annotations.EnableFlags;
import android.provider.Settings;
import android.testing.TestableResources;
-import android.util.KeyValueListParser;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.TextView;
import androidx.test.annotation.UiThreadTest;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
+import com.android.systemui.Flags;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.animation.AnimatorTestRule;
+import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper.SnoozeOption;
import com.android.systemui.res.R;
+import org.junit.After;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.util.ArrayList;
+import java.util.List;
@SmallTest
@RunWith(AndroidJUnit4.class)
@@ -46,8 +61,12 @@ import java.util.ArrayList;
public class NotificationSnoozeTest extends SysuiTestCase {
private static final int RES_DEFAULT = 2;
private static final int[] RES_OPTIONS = {1, 2, 3};
- private NotificationSnooze mNotificationSnooze;
- private KeyValueListParser mMockParser;
+ private final NotificationSwipeActionHelper mSnoozeListener = mock(
+ NotificationSwipeActionHelper.class);
+ private NotificationSnooze mUnderTest;
+
+ @Rule
+ public final AnimatorTestRule mAnimatorTestRule = new AnimatorTestRule(this);
@Before
public void setUp() throws Exception {
@@ -56,62 +75,117 @@ public class NotificationSnoozeTest extends SysuiTestCase {
TestableResources resources = mContext.getOrCreateTestableResources();
resources.addOverride(R.integer.config_notification_snooze_time_default, RES_DEFAULT);
resources.addOverride(R.array.config_notification_snooze_times, RES_OPTIONS);
- mNotificationSnooze = new NotificationSnooze(mContext, null);
- mMockParser = mock(KeyValueListParser.class);
+
+ mUnderTest = new NotificationSnooze(mContext, null);
+ mUnderTest.setSnoozeListener(mSnoozeListener);
+ mUnderTest.mExpandButton = mock(ImageView.class);
+ mUnderTest.mSnoozeView = mock(View.class);
+ mUnderTest.mSelectedOptionText = mock(TextView.class);
+ mUnderTest.mDivider = mock(View.class);
+ mUnderTest.mSnoozeOptionContainer = mock(ViewGroup.class);
+ mUnderTest.mSnoozeOptions = mock(List.class);
+ }
+
+ @After
+ public void tearDown() {
+ // Make sure all animations are finished
+ mAnimatorTestRule.advanceTimeBy(1000L);
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_NOTIFICATION_UNDO_GUTS_ON_CONFIG_CHANGED)
+ public void closeControls_withoutSave_performsUndo() {
+ ArrayList<SnoozeOption> options = mUnderTest.getDefaultSnoozeOptions();
+ mUnderTest.mSelectedOption = options.getFirst();
+ mUnderTest.showSnoozeOptions(true);
+
+ assertThat(
+ mUnderTest.handleCloseControls(/* save = */ false, /* force = */ false)).isFalse();
+
+ assertThat(mUnderTest.mSelectedOption).isNull();
+ assertThat(mUnderTest.isExpanded()).isFalse();
+ verify(mSnoozeListener, times(0)).snooze(any(), any());
+ }
+
+ @Test
+ public void closeControls_whenExpanded_collapsesOptions() {
+ ArrayList<SnoozeOption> options = mUnderTest.getDefaultSnoozeOptions();
+ mUnderTest.mSelectedOption = options.getFirst();
+ mUnderTest.showSnoozeOptions(true);
+
+ assertThat(mUnderTest.handleCloseControls(/* save = */ true, /* force = */ false)).isTrue();
+
+ assertThat(mUnderTest.mSelectedOption).isNotNull();
+ assertThat(mUnderTest.isExpanded()).isFalse();
+ }
+
+ @Test
+ public void closeControls_whenCollapsed_commitsChanges() {
+ ArrayList<SnoozeOption> options = mUnderTest.getDefaultSnoozeOptions();
+ mUnderTest.mSelectedOption = options.getFirst();
+
+ assertThat(mUnderTest.handleCloseControls(/* save = */ true, /* force = */ false)).isTrue();
+
+ verify(mSnoozeListener).snooze(any(), any());
+ }
+
+ @Test
+ public void closeControls_withForce_returnsFalse() {
+ assertThat(mUnderTest.handleCloseControls(/* save = */ true, /* force = */ true)).isFalse();
}
@Test
- public void testGetOptionsWithNoConfig() throws Exception {
- ArrayList<SnoozeOption> result = mNotificationSnooze.getDefaultSnoozeOptions();
+ public void testGetOptionsWithNoConfig() {
+ ArrayList<SnoozeOption> result = mUnderTest.getDefaultSnoozeOptions();
assertEquals(3, result.size());
assertEquals(1, result.get(0).getMinutesToSnoozeFor()); // respect order
assertEquals(2, result.get(1).getMinutesToSnoozeFor());
assertEquals(3, result.get(2).getMinutesToSnoozeFor());
- assertEquals(2, mNotificationSnooze.getDefaultOption().getMinutesToSnoozeFor());
+ assertEquals(2, mUnderTest.getDefaultOption().getMinutesToSnoozeFor());
}
@Test
- public void testGetOptionsWithInvalidConfig() throws Exception {
+ public void testGetOptionsWithInvalidConfig() {
Settings.Global.putString(mContext.getContentResolver(),
Settings.Global.NOTIFICATION_SNOOZE_OPTIONS,
"this is garbage");
- ArrayList<SnoozeOption> result = mNotificationSnooze.getDefaultSnoozeOptions();
+ ArrayList<SnoozeOption> result = mUnderTest.getDefaultSnoozeOptions();
assertEquals(3, result.size());
assertEquals(1, result.get(0).getMinutesToSnoozeFor()); // respect order
assertEquals(2, result.get(1).getMinutesToSnoozeFor());
assertEquals(3, result.get(2).getMinutesToSnoozeFor());
- assertEquals(2, mNotificationSnooze.getDefaultOption().getMinutesToSnoozeFor());
+ assertEquals(2, mUnderTest.getDefaultOption().getMinutesToSnoozeFor());
}
@Test
- public void testGetOptionsWithValidDefault() throws Exception {
+ public void testGetOptionsWithValidDefault() {
Settings.Global.putString(mContext.getContentResolver(),
Settings.Global.NOTIFICATION_SNOOZE_OPTIONS,
"default=10,options_array=4:5:6:7");
- ArrayList<SnoozeOption> result = mNotificationSnooze.getDefaultSnoozeOptions();
- assertNotNull(mNotificationSnooze.getDefaultOption()); // pick one
+ ArrayList<SnoozeOption> result = mUnderTest.getDefaultSnoozeOptions();
+ assertNotNull(mUnderTest.getDefaultOption()); // pick one
}
@Test
- public void testGetOptionsWithValidConfig() throws Exception {
+ public void testGetOptionsWithValidConfig() {
Settings.Global.putString(mContext.getContentResolver(),
Settings.Global.NOTIFICATION_SNOOZE_OPTIONS,
"default=6,options_array=4:5:6:7");
- ArrayList<SnoozeOption> result = mNotificationSnooze.getDefaultSnoozeOptions();
+ ArrayList<SnoozeOption> result = mUnderTest.getDefaultSnoozeOptions();
assertEquals(4, result.size());
assertEquals(4, result.get(0).getMinutesToSnoozeFor()); // respect order
assertEquals(5, result.get(1).getMinutesToSnoozeFor());
assertEquals(6, result.get(2).getMinutesToSnoozeFor());
assertEquals(7, result.get(3).getMinutesToSnoozeFor());
- assertEquals(6, mNotificationSnooze.getDefaultOption().getMinutesToSnoozeFor());
+ assertEquals(6, mUnderTest.getDefaultOption().getMinutesToSnoozeFor());
}
@Test
- public void testGetOptionsWithLongConfig() throws Exception {
+ public void testGetOptionsWithLongConfig() {
Settings.Global.putString(mContext.getContentResolver(),
Settings.Global.NOTIFICATION_SNOOZE_OPTIONS,
"default=6,options_array=4:5:6:7:8:9:10:11:12:13:14:15:16:17");
- ArrayList<SnoozeOption> result = mNotificationSnooze.getDefaultSnoozeOptions();
+ ArrayList<SnoozeOption> result = mUnderTest.getDefaultSnoozeOptions();
assertTrue(result.size() > 3);
assertEquals(4, result.get(0).getMinutesToSnoozeFor()); // respect order
assertEquals(5, result.get(1).getMinutesToSnoozeFor());
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/DisplaySwitchLatencyTrackerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/DisplaySwitchLatencyTrackerTest.kt
index 09be93de9f3e..ea91b7a9d6e2 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/DisplaySwitchLatencyTrackerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/DisplaySwitchLatencyTrackerTest.kt
@@ -19,6 +19,7 @@ package com.android.systemui.unfold
import android.content.Context
import android.content.res.Resources
import android.hardware.devicestate.DeviceStateManager
+import android.os.PowerManager.GO_TO_SLEEP_REASON_DEVICE_FOLD
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.R
@@ -27,16 +28,20 @@ import com.android.systemui.common.ui.data.repository.ConfigurationRepositoryImp
import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractorImpl
import com.android.systemui.defaultDeviceState
import com.android.systemui.deviceStateManager
-import com.android.systemui.display.data.repository.DeviceStateRepository
import com.android.systemui.display.data.repository.DeviceStateRepository.DeviceState
+import com.android.systemui.display.data.repository.DeviceStateRepository.DeviceState.FOLDED
+import com.android.systemui.display.data.repository.DeviceStateRepository.DeviceState.HALF_FOLDED
+import com.android.systemui.display.data.repository.DeviceStateRepository.DeviceState.UNFOLDED
+import com.android.systemui.display.data.repository.fakeDeviceStateRepository
import com.android.systemui.foldedDeviceStateList
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.power.domain.interactor.PowerInteractor
-import com.android.systemui.power.shared.model.ScreenPowerState
-import com.android.systemui.power.shared.model.WakeSleepReason
-import com.android.systemui.power.shared.model.WakefulnessModel
-import com.android.systemui.power.shared.model.WakefulnessState
+import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAsleepForTest
+import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAwakeForTest
+import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setScreenPowerState
+import com.android.systemui.power.domain.interactor.PowerInteractorFactory
+import com.android.systemui.power.shared.model.ScreenPowerState.SCREEN_OFF
+import com.android.systemui.power.shared.model.ScreenPowerState.SCREEN_ON
import com.android.systemui.shared.system.SysUiStatsLog
import com.android.systemui.statusbar.policy.FakeConfigurationController
import com.android.systemui.unfold.DisplaySwitchLatencyTracker.Companion.FOLDABLE_DEVICE_STATE_CLOSED
@@ -45,7 +50,7 @@ import com.android.systemui.unfold.DisplaySwitchLatencyTracker.DisplaySwitchLate
import com.android.systemui.unfold.data.repository.UnfoldTransitionRepositoryImpl
import com.android.systemui.unfold.domain.interactor.UnfoldTransitionInteractor
import com.android.systemui.unfoldedDeviceState
-import com.android.systemui.util.animation.data.repository.AnimationStatusRepository
+import com.android.systemui.util.animation.data.repository.fakeAnimationStatusRepository
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.capture
import com.android.systemui.util.time.FakeSystemClock
@@ -77,14 +82,15 @@ class DisplaySwitchLatencyTrackerTest : SysuiTestCase() {
private lateinit var displaySwitchLatencyTracker: DisplaySwitchLatencyTracker
@Captor private lateinit var loggerArgumentCaptor: ArgumentCaptor<DisplaySwitchLatencyEvent>
+ private val kosmos = Kosmos()
private val mockContext = mock<Context>()
private val resources = mock<Resources>()
- private val foldStateRepository = mock<DeviceStateRepository>()
- private val powerInteractor = mock<PowerInteractor>()
- private val animationStatusRepository = mock<AnimationStatusRepository>()
+ private val foldStateRepository = kosmos.fakeDeviceStateRepository
+ private val powerInteractor = PowerInteractorFactory.create().powerInteractor
+ private val animationStatusRepository = kosmos.fakeAnimationStatusRepository
private val keyguardInteractor = mock<KeyguardInteractor>()
private val displaySwitchLatencyLogger = mock<DisplaySwitchLatencyLogger>()
- private val kosmos = Kosmos()
+
private val deviceStateManager = kosmos.deviceStateManager
private val closedDeviceState = kosmos.foldedDeviceStateList.first()
private val openDeviceState = kosmos.unfoldedDeviceState
@@ -94,12 +100,7 @@ class DisplaySwitchLatencyTrackerTest : SysuiTestCase() {
private val testDispatcher: TestDispatcher = StandardTestDispatcher()
private val testScope: TestScope = TestScope(testDispatcher)
- private val isAsleep = MutableStateFlow(false)
private val isAodAvailable = MutableStateFlow(false)
- private val deviceState = MutableStateFlow(DeviceState.UNFOLDED)
- private val screenPowerState = MutableStateFlow(ScreenPowerState.SCREEN_ON)
- private val areAnimationEnabled = MutableStateFlow(true)
- private val lastWakefulnessEvent = MutableStateFlow(WakefulnessModel())
private val systemClock = FakeSystemClock()
private val configurationController = FakeConfigurationController()
private val configurationRepository =
@@ -126,13 +127,10 @@ class DisplaySwitchLatencyTrackerTest : SysuiTestCase() {
.thenReturn(listOf(closedDeviceState, openDeviceState))
whenever(resources.getIntArray(R.array.config_foldedDeviceStates))
.thenReturn(nonEmptyClosedDeviceStatesArray)
- whenever(foldStateRepository.state).thenReturn(deviceState)
- whenever(powerInteractor.isAsleep).thenReturn(isAsleep)
- whenever(animationStatusRepository.areAnimationsEnabled()).thenReturn(areAnimationEnabled)
- whenever(powerInteractor.screenPowerState).thenReturn(screenPowerState)
whenever(keyguardInteractor.isAodAvailable).thenReturn(isAodAvailable)
- whenever(powerInteractor.detailedWakefulness).thenReturn(lastWakefulnessEvent)
-
+ animationStatusRepository.onAnimationStatusChanged(true)
+ powerInteractor.setAwakeForTest()
+ powerInteractor.setScreenPowerState(SCREEN_ON)
displaySwitchLatencyTracker =
DisplaySwitchLatencyTracker(
mockContext,
@@ -152,21 +150,19 @@ class DisplaySwitchLatencyTrackerTest : SysuiTestCase() {
@Test
fun unfold_logsLatencyTillTransitionStarted() {
testScope.runTest {
- areAnimationEnabled.emit(true)
-
displaySwitchLatencyTracker.start()
- deviceState.emit(DeviceState.FOLDED)
- screenPowerState.emit(ScreenPowerState.SCREEN_OFF)
+ setDeviceState(FOLDED)
+ powerInteractor.setScreenPowerState(SCREEN_OFF)
systemClock.advanceTime(50)
runCurrent()
- deviceState.emit(DeviceState.HALF_FOLDED)
+ setDeviceState(HALF_FOLDED)
runCurrent()
systemClock.advanceTime(50)
- screenPowerState.emit(ScreenPowerState.SCREEN_ON)
+ powerInteractor.setScreenPowerState(SCREEN_ON)
systemClock.advanceTime(200)
unfoldTransitionProgressProvider.onTransitionStarted()
runCurrent()
- deviceState.emit(DeviceState.UNFOLDED)
+ setDeviceState(UNFOLDED)
verify(displaySwitchLatencyLogger).log(capture(loggerArgumentCaptor))
val loggedEvent = loggerArgumentCaptor.value
@@ -202,23 +198,22 @@ class DisplaySwitchLatencyTrackerTest : SysuiTestCase() {
systemClock,
deviceStateManager,
)
- areAnimationEnabled.emit(true)
displaySwitchLatencyTracker.start()
- deviceState.emit(DeviceState.FOLDED)
- screenPowerState.emit(ScreenPowerState.SCREEN_OFF)
+ setDeviceState(FOLDED)
+ powerInteractor.setScreenPowerState(SCREEN_OFF)
systemClock.advanceTime(50)
runCurrent()
- deviceState.emit(DeviceState.HALF_FOLDED)
+ setDeviceState(HALF_FOLDED)
systemClock.advanceTime(50)
runCurrent()
- screenPowerState.emit(ScreenPowerState.SCREEN_ON)
+ powerInteractor.setScreenPowerState(SCREEN_ON)
systemClock.advanceTime(50)
runCurrent()
systemClock.advanceTime(200)
unfoldTransitionProgressProvider.onTransitionStarted()
runCurrent()
- deviceState.emit(DeviceState.UNFOLDED)
+ setDeviceState(UNFOLDED)
verify(displaySwitchLatencyLogger).log(capture(loggerArgumentCaptor))
val loggedEvent = loggerArgumentCaptor.value
@@ -235,23 +230,23 @@ class DisplaySwitchLatencyTrackerTest : SysuiTestCase() {
@Test
fun unfold_animationDisabled_logsLatencyTillScreenTurnedOn() {
testScope.runTest {
- areAnimationEnabled.emit(false)
+ animationStatusRepository.onAnimationStatusChanged(false)
displaySwitchLatencyTracker.start()
- deviceState.emit(DeviceState.FOLDED)
- screenPowerState.emit(ScreenPowerState.SCREEN_OFF)
+ setDeviceState(FOLDED)
+ powerInteractor.setScreenPowerState(SCREEN_OFF)
systemClock.advanceTime(50)
runCurrent()
- deviceState.emit(DeviceState.HALF_FOLDED)
+ setDeviceState(HALF_FOLDED)
systemClock.advanceTime(50)
runCurrent()
- screenPowerState.emit(ScreenPowerState.SCREEN_ON)
+ powerInteractor.setScreenPowerState(SCREEN_ON)
systemClock.advanceTime(50)
runCurrent()
unfoldTransitionProgressProvider.onTransitionStarted()
systemClock.advanceTime(200)
runCurrent()
- deviceState.emit(DeviceState.UNFOLDED)
+ setDeviceState(UNFOLDED)
verify(displaySwitchLatencyLogger).log(capture(loggerArgumentCaptor))
val loggedEvent = loggerArgumentCaptor.value
@@ -268,19 +263,18 @@ class DisplaySwitchLatencyTrackerTest : SysuiTestCase() {
@Test
fun foldWhileStayingAwake_logsLatency() {
testScope.runTest {
- areAnimationEnabled.emit(true)
- deviceState.emit(DeviceState.UNFOLDED)
- screenPowerState.emit(ScreenPowerState.SCREEN_ON)
+ setDeviceState(UNFOLDED)
+ powerInteractor.setScreenPowerState(SCREEN_ON)
displaySwitchLatencyTracker.start()
- deviceState.emit(DeviceState.HALF_FOLDED)
+ setDeviceState(HALF_FOLDED)
systemClock.advanceTime(50)
runCurrent()
- deviceState.emit(DeviceState.FOLDED)
- screenPowerState.emit(ScreenPowerState.SCREEN_OFF)
+ setDeviceState(FOLDED)
+ powerInteractor.setScreenPowerState(SCREEN_OFF)
runCurrent()
systemClock.advanceTime(200)
- screenPowerState.emit(ScreenPowerState.SCREEN_ON)
+ powerInteractor.setScreenPowerState(SCREEN_ON)
runCurrent()
verify(displaySwitchLatencyLogger).log(capture(loggerArgumentCaptor))
@@ -298,25 +292,19 @@ class DisplaySwitchLatencyTrackerTest : SysuiTestCase() {
@Test
fun foldToAod_capturesToStateAsAod() {
testScope.runTest {
- areAnimationEnabled.emit(true)
- deviceState.emit(DeviceState.UNFOLDED)
+ setDeviceState(UNFOLDED)
isAodAvailable.emit(true)
displaySwitchLatencyTracker.start()
- deviceState.emit(DeviceState.HALF_FOLDED)
+ setDeviceState(HALF_FOLDED)
systemClock.advanceTime(50)
runCurrent()
- deviceState.emit(DeviceState.FOLDED)
- lastWakefulnessEvent.emit(
- WakefulnessModel(
- internalWakefulnessState = WakefulnessState.ASLEEP,
- lastSleepReason = WakeSleepReason.FOLD,
- )
- )
- screenPowerState.emit(ScreenPowerState.SCREEN_OFF)
+ setDeviceState(FOLDED)
+ powerInteractor.setAsleepForTest(sleepReason = GO_TO_SLEEP_REASON_DEVICE_FOLD)
+ powerInteractor.setScreenPowerState(SCREEN_OFF)
runCurrent()
systemClock.advanceTime(200)
- screenPowerState.emit(ScreenPowerState.SCREEN_ON)
+ powerInteractor.setScreenPowerState(SCREEN_ON)
runCurrent()
verify(displaySwitchLatencyLogger).log(capture(loggerArgumentCaptor))
@@ -335,22 +323,21 @@ class DisplaySwitchLatencyTrackerTest : SysuiTestCase() {
@Test
fun fold_notAFoldable_shouldNotLogLatency() {
testScope.runTest {
- areAnimationEnabled.emit(true)
- deviceState.emit(DeviceState.UNFOLDED)
+ setDeviceState(UNFOLDED)
whenever(resources.getIntArray(R.array.config_foldedDeviceStates))
.thenReturn(IntArray(0))
whenever(deviceStateManager.supportedDeviceStates)
.thenReturn(listOf(defaultDeviceState))
displaySwitchLatencyTracker.start()
- deviceState.emit(DeviceState.HALF_FOLDED)
+ setDeviceState(HALF_FOLDED)
systemClock.advanceTime(50)
runCurrent()
- deviceState.emit(DeviceState.FOLDED)
- screenPowerState.emit(ScreenPowerState.SCREEN_OFF)
+ setDeviceState(FOLDED)
+ powerInteractor.setScreenPowerState(SCREEN_OFF)
runCurrent()
systemClock.advanceTime(200)
- screenPowerState.emit(ScreenPowerState.SCREEN_ON)
+ powerInteractor.setScreenPowerState(SCREEN_ON)
runCurrent()
verify(displaySwitchLatencyLogger, never()).log(any())
@@ -360,22 +347,16 @@ class DisplaySwitchLatencyTrackerTest : SysuiTestCase() {
@Test
fun foldToScreenOff_capturesToStateAsScreenOff() {
testScope.runTest {
- areAnimationEnabled.emit(true)
- deviceState.emit(DeviceState.UNFOLDED)
+ setDeviceState(UNFOLDED)
isAodAvailable.emit(false)
displaySwitchLatencyTracker.start()
- deviceState.emit(DeviceState.HALF_FOLDED)
+ setDeviceState(HALF_FOLDED)
systemClock.advanceTime(50)
runCurrent()
- deviceState.emit(DeviceState.FOLDED)
- lastWakefulnessEvent.emit(
- WakefulnessModel(
- internalWakefulnessState = WakefulnessState.ASLEEP,
- lastSleepReason = WakeSleepReason.FOLD,
- )
- )
- screenPowerState.emit(ScreenPowerState.SCREEN_OFF)
+ setDeviceState(FOLDED)
+ powerInteractor.setAsleepForTest(sleepReason = GO_TO_SLEEP_REASON_DEVICE_FOLD)
+ powerInteractor.setScreenPowerState(SCREEN_OFF)
runCurrent()
verify(displaySwitchLatencyLogger).log(capture(loggerArgumentCaptor))
@@ -390,4 +371,8 @@ class DisplaySwitchLatencyTrackerTest : SysuiTestCase() {
assertThat(loggedEvent).isEqualTo(expectedLoggedEvent)
}
}
+
+ private suspend fun setDeviceState(state: DeviceState) {
+ foldStateRepository.emit(state)
+ }
}
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 3b89e9c42c93..84c859ccd5a9 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1359,6 +1359,8 @@
<string name="hub_onboarding_bottom_sheet_text">Access your favorite widgets and screen savers while charging.</string>
<!-- Hub onboarding bottom sheet action button title. [CHAR LIMIT=NONE] -->
<string name="hub_onboarding_bottom_sheet_action_button">Let\u2019s go</string>
+ <!-- Text for a tooltip that appears over the "show screensaver" button on glanceable hub. [CHAR LIMIT=NONE] -->
+ <string name="glanceable_hub_to_dream_button_tooltip">Show your favorite screensavers while charging</string>
<!-- Related to user switcher --><skip/>
diff --git a/packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt b/packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt
index 1c994731c393..126471234fa1 100644
--- a/packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt
@@ -32,6 +32,9 @@ import com.android.systemui.authentication.shared.model.AuthenticationWipeModel.
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.log.table.TableLogBuffer
+import com.android.systemui.log.table.logDiffsForTable
+import com.android.systemui.scene.domain.SceneFrameworkTableLog
import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import com.android.systemui.util.time.SystemClock
import javax.inject.Inject
@@ -66,6 +69,7 @@ constructor(
@Background private val backgroundDispatcher: CoroutineDispatcher,
private val repository: AuthenticationRepository,
private val selectedUserInteractor: SelectedUserInteractor,
+ @SceneFrameworkTableLog private val tableLogBuffer: TableLogBuffer,
) {
/**
* The currently-configured authentication method. This determines how the authentication
@@ -85,7 +89,11 @@ constructor(
* `true` even when the lockscreen is showing and still needs to be dismissed by the user to
* proceed.
*/
- val authenticationMethod: Flow<AuthenticationMethodModel> = repository.authenticationMethod
+ val authenticationMethod: Flow<AuthenticationMethodModel> =
+ repository.authenticationMethod.logDiffsForTable(
+ tableLogBuffer = tableLogBuffer,
+ initialValue = AuthenticationMethodModel.None,
+ )
/**
* Whether the auto confirm feature is enabled for the currently-selected user.
diff --git a/packages/SystemUI/src/com/android/systemui/authentication/shared/model/AuthenticationMethodModel.kt b/packages/SystemUI/src/com/android/systemui/authentication/shared/model/AuthenticationMethodModel.kt
index 4e45fcc25fb8..744fd7e94ab4 100644
--- a/packages/SystemUI/src/com/android/systemui/authentication/shared/model/AuthenticationMethodModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/authentication/shared/model/AuthenticationMethodModel.kt
@@ -16,6 +16,9 @@
package com.android.systemui.authentication.shared.model
+import com.android.systemui.log.table.Diffable
+import com.android.systemui.log.table.TableRowLogger
+
/** Enumerates all known authentication methods. */
sealed class AuthenticationMethodModel(
/**
@@ -24,8 +27,8 @@ sealed class AuthenticationMethodModel(
* "Secure" authentication methods require authentication to unlock the device. Non-secure auth
* methods simply require user dismissal.
*/
- open val isSecure: Boolean,
-) {
+ open val isSecure: Boolean
+) : Diffable<AuthenticationMethodModel> {
/**
* Device doesn't use a secure authentication method. Either there is no lockscreen or the lock
* screen can be swiped away when displayed.
@@ -39,4 +42,8 @@ sealed class AuthenticationMethodModel(
data object Pattern : AuthenticationMethodModel(isSecure = true)
data object Sim : AuthenticationMethodModel(isSecure = true)
+
+ override fun logDiffs(prevVal: AuthenticationMethodModel, row: TableRowLogger) {
+ row.logChange(columnName = "authenticationMethod", value = toString())
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/common/data/CommonDataLayerModule.kt b/packages/SystemUI/src/com/android/systemui/common/data/CommonDataLayerModule.kt
index 19238804fb12..79e66a89cd6b 100644
--- a/packages/SystemUI/src/com/android/systemui/common/data/CommonDataLayerModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/common/data/CommonDataLayerModule.kt
@@ -16,6 +16,8 @@
package com.android.systemui.common.data
+import com.android.systemui.common.data.repository.BatteryRepository
+import com.android.systemui.common.data.repository.BatteryRepositoryImpl
import com.android.systemui.common.data.repository.PackageChangeRepository
import com.android.systemui.common.data.repository.PackageChangeRepositoryImpl
import dagger.Binds
@@ -27,4 +29,6 @@ abstract class CommonDataLayerModule {
abstract fun bindPackageChangeRepository(
impl: PackageChangeRepositoryImpl
): PackageChangeRepository
+
+ @Binds abstract fun bindBatteryRepository(impl: BatteryRepositoryImpl): BatteryRepository
}
diff --git a/packages/SystemUI/src/com/android/systemui/common/data/repository/BatteryRepository.kt b/packages/SystemUI/src/com/android/systemui/common/data/repository/BatteryRepository.kt
new file mode 100644
index 000000000000..63b051339d4b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/common/data/repository/BatteryRepository.kt
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.common.data.repository
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.statusbar.policy.BatteryController
+import com.android.systemui.util.kotlin.isDevicePluggedIn
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.stateIn
+
+interface BatteryRepository {
+ val isDevicePluggedIn: Flow<Boolean>
+}
+
+@SysUISingleton
+class BatteryRepositoryImpl
+@Inject
+constructor(@Background bgScope: CoroutineScope, batteryController: BatteryController) :
+ BatteryRepository {
+
+ /** Returns {@code true} if the device is currently plugged in or wireless charging. */
+ override val isDevicePluggedIn: Flow<Boolean> =
+ batteryController
+ .isDevicePluggedIn()
+ .stateIn(bgScope, SharingStarted.WhileSubscribed(), batteryController.isPluggedIn)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/common/domain/interactor/BatteryInteractor.kt b/packages/SystemUI/src/com/android/systemui/common/domain/interactor/BatteryInteractor.kt
new file mode 100644
index 000000000000..987776d14b2b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/common/domain/interactor/BatteryInteractor.kt
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.common.domain.interactor
+
+import com.android.systemui.common.data.repository.BatteryRepository
+import com.android.systemui.dagger.SysUISingleton
+import javax.inject.Inject
+
+@SysUISingleton
+class BatteryInteractor @Inject constructor(batteryRepository: BatteryRepository) {
+ val isDevicePluggedIn = batteryRepository.isDevicePluggedIn
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/DevicePosturingListener.kt b/packages/SystemUI/src/com/android/systemui/communal/DevicePosturingListener.kt
index 47040fa4a572..af8a5fa23ccb 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/DevicePosturingListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/DevicePosturingListener.kt
@@ -58,7 +58,6 @@ constructor(
.distinctUntilChanged()
.logDiffsForTable(
tableLogBuffer = tableLogBuffer,
- columnPrefix = "",
columnName = "postured",
initialValue = false,
)
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalMediaRepository.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalMediaRepository.kt
index 882991aacdbb..b89d32244e17 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalMediaRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalMediaRepository.kt
@@ -52,7 +52,6 @@ constructor(
override val mediaModel: Flow<CommunalMediaModel> =
_mediaModel.logDiffsForTable(
tableLogBuffer = tableLogBuffer,
- columnPrefix = "",
initialValue = CommunalMediaModel.INACTIVE,
)
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalPrefsRepository.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalPrefsRepository.kt
index 090264678f35..b7476900d784 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalPrefsRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalPrefsRepository.kt
@@ -56,6 +56,12 @@ interface CommunalPrefsRepository {
/** Save the hub onboarding dismissed state for the current user. */
suspend fun setHubOnboardingDismissed(user: UserInfo)
+
+ /** Whether dream button tooltip has been dismissed. */
+ fun isDreamButtonTooltipDismissed(user: UserInfo): Flow<Boolean>
+
+ /** Save the dream button tooltip dismissed state for the current user. */
+ suspend fun setDreamButtonTooltipDismissed(user: UserInfo)
}
@SysUISingleton
@@ -87,27 +93,34 @@ constructor(
readKeyForUser(user, CTA_DISMISSED_STATE)
override suspend fun setCtaDismissed(user: UserInfo) =
- withContext(bgDispatcher) {
- getSharedPrefsForUser(user).edit().putBoolean(CTA_DISMISSED_STATE, true).apply()
- logger.i("Dismissed CTA tile")
- }
+ setBooleanKeyValueForUser(user, CTA_DISMISSED_STATE, "Dismissed CTA tile")
override fun isHubOnboardingDismissed(user: UserInfo): Flow<Boolean> =
readKeyForUser(user, HUB_ONBOARDING_DISMISSED_STATE)
override suspend fun setHubOnboardingDismissed(user: UserInfo) =
- withContext(bgDispatcher) {
- getSharedPrefsForUser(user)
- .edit()
- .putBoolean(HUB_ONBOARDING_DISMISSED_STATE, true)
- .apply()
- logger.i("Dismissed hub onboarding")
- }
+ setBooleanKeyValueForUser(user, HUB_ONBOARDING_DISMISSED_STATE, "Dismissed hub onboarding")
+
+ override fun isDreamButtonTooltipDismissed(user: UserInfo): Flow<Boolean> =
+ readKeyForUser(user, DREAM_BUTTON_TOOLTIP_DISMISSED_STATE)
+
+ override suspend fun setDreamButtonTooltipDismissed(user: UserInfo) =
+ setBooleanKeyValueForUser(
+ user,
+ DREAM_BUTTON_TOOLTIP_DISMISSED_STATE,
+ "Dismissed dream button tooltip",
+ )
private fun getSharedPrefsForUser(user: UserInfo): SharedPreferences {
return userFileManager.getSharedPreferences(FILE_NAME, Context.MODE_PRIVATE, user.id)
}
+ private suspend fun setBooleanKeyValueForUser(user: UserInfo, key: String, logMsg: String) =
+ withContext(bgDispatcher) {
+ getSharedPrefsForUser(user).edit().putBoolean(key, true).apply()
+ logger.i(logMsg)
+ }
+
private fun readKeyForUser(user: UserInfo, key: String): Flow<Boolean> {
return backupRestorationEvents
.flatMapLatest {
@@ -122,5 +135,6 @@ constructor(
const val FILE_NAME = "communal_hub_prefs"
const val CTA_DISMISSED_STATE = "cta_dismissed"
const val HUB_ONBOARDING_DISMISSED_STATE = "hub_onboarding_dismissed"
+ const val DREAM_BUTTON_TOOLTIP_DISMISSED_STATE = "dream_button_tooltip_dismissed_state"
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSettingsRepository.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSettingsRepository.kt
index 53122c56ed2c..abd101693b43 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSettingsRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSettingsRepository.kt
@@ -34,6 +34,7 @@ import com.android.systemui.communal.data.model.DisabledReason.DISABLED_REASON_I
import com.android.systemui.communal.data.model.DisabledReason.DISABLED_REASON_USER_SETTING
import com.android.systemui.communal.data.repository.CommunalSettingsRepositoryModule.Companion.DEFAULT_BACKGROUND_TYPE
import com.android.systemui.communal.shared.model.CommunalBackgroundType
+import com.android.systemui.communal.shared.model.WhenToDream
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
@@ -60,6 +61,12 @@ interface CommunalSettingsRepository {
fun getScreensaverEnabledState(user: UserInfo): Flow<Boolean>
/**
+ * Returns a [WhenToDream] for the specified user, indicating what state the device should be in
+ * to trigger dreams.
+ */
+ fun getWhenToDreamState(user: UserInfo): Flow<WhenToDream>
+
+ /**
* Returns true if any glanceable hub functionality should be enabled via configs and flags.
*
* This should be used for preventing basic glanceable hub functionality from running on devices
@@ -157,6 +164,49 @@ constructor(
}
.flowOn(bgDispatcher)
+ override fun getWhenToDreamState(user: UserInfo): Flow<WhenToDream> =
+ secureSettings
+ .observerFlow(
+ userId = user.id,
+ names =
+ arrayOf(
+ Settings.Secure.SCREENSAVER_ACTIVATE_ON_SLEEP,
+ Settings.Secure.SCREENSAVER_ACTIVATE_ON_DOCK,
+ Settings.Secure.SCREENSAVER_ACTIVATE_ON_POSTURED,
+ ),
+ )
+ .emitOnStart()
+ .map {
+ if (
+ secureSettings.getIntForUser(
+ Settings.Secure.SCREENSAVER_ACTIVATE_ON_SLEEP,
+ 0,
+ user.id,
+ ) == 1
+ ) {
+ WhenToDream.WHILE_CHARGING
+ } else if (
+ secureSettings.getIntForUser(
+ Settings.Secure.SCREENSAVER_ACTIVATE_ON_DOCK,
+ 0,
+ user.id,
+ ) == 1
+ ) {
+ WhenToDream.WHILE_DOCKED
+ } else if (
+ secureSettings.getIntForUser(
+ Settings.Secure.SCREENSAVER_ACTIVATE_ON_POSTURED,
+ 0,
+ user.id,
+ ) == 1
+ ) {
+ WhenToDream.WHILE_POSTURED
+ } else {
+ WhenToDream.NEVER
+ }
+ }
+ .flowOn(bgDispatcher)
+
override fun getAllowedByDevicePolicy(user: UserInfo): Flow<Boolean> =
broadcastDispatcher
.broadcastFlow(
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalTutorialRepository.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalTutorialRepository.kt
index 19666e4df1cf..1b0a6a06caa3 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalTutorialRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalTutorialRepository.kt
@@ -98,7 +98,6 @@ constructor(
.filterNotNull()
.logDiffsForTable(
tableLogBuffer = tableLogBuffer,
- columnPrefix = "",
columnName = "tutorialSettingState",
initialValue = HUB_MODE_TUTORIAL_NOT_STARTED,
)
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
index 74c335e79d4e..b4e6e9348b3d 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
@@ -30,11 +30,13 @@ import com.android.compose.animation.scene.TransitionKey
import com.android.systemui.Flags.communalResponsiveGrid
import com.android.systemui.Flags.glanceableHubBlurredBackground
import com.android.systemui.broadcast.BroadcastDispatcher
+import com.android.systemui.common.domain.interactor.BatteryInteractor
import com.android.systemui.communal.data.repository.CommunalMediaRepository
import com.android.systemui.communal.data.repository.CommunalSmartspaceRepository
import com.android.systemui.communal.data.repository.CommunalWidgetRepository
import com.android.systemui.communal.domain.model.CommunalContentModel
import com.android.systemui.communal.domain.model.CommunalContentModel.WidgetContent
+import com.android.systemui.communal.posturing.domain.interactor.PosturingInteractor
import com.android.systemui.communal.shared.model.CommunalBackgroundType
import com.android.systemui.communal.shared.model.CommunalContentSize
import com.android.systemui.communal.shared.model.CommunalContentSize.FixedSize.FULL
@@ -43,11 +45,14 @@ import com.android.systemui.communal.shared.model.CommunalContentSize.FixedSize.
import com.android.systemui.communal.shared.model.CommunalScenes
import com.android.systemui.communal.shared.model.CommunalWidgetContentModel
import com.android.systemui.communal.shared.model.EditModeState
+import com.android.systemui.communal.shared.model.WhenToDream
import com.android.systemui.communal.widgets.EditWidgetsActivityStarter
import com.android.systemui.communal.widgets.WidgetConfigurator
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.dock.DockManager
+import com.android.systemui.dock.retrieveIsDocked
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.Edge
@@ -67,6 +72,7 @@ import com.android.systemui.statusbar.phone.ManagedProfileController
import com.android.systemui.util.kotlin.BooleanFlowOperators.allOf
import com.android.systemui.util.kotlin.BooleanFlowOperators.not
import com.android.systemui.util.kotlin.emitOnStart
+import com.android.systemui.util.kotlin.isDevicePluggedIn
import javax.inject.Inject
import kotlin.time.Duration.Companion.minutes
import kotlinx.coroutines.CoroutineDispatcher
@@ -86,6 +92,7 @@ import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flow
+import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
@@ -117,6 +124,9 @@ constructor(
@CommunalLog logBuffer: LogBuffer,
@CommunalTableLog tableLogBuffer: TableLogBuffer,
private val managedProfileController: ManagedProfileController,
+ private val batteryInteractor: BatteryInteractor,
+ private val dockManager: DockManager,
+ private val posturingInteractor: PosturingInteractor,
) {
private val logger = Logger(logBuffer, "CommunalInteractor")
@@ -163,7 +173,6 @@ constructor(
}
.logDiffsForTable(
tableLogBuffer = tableLogBuffer,
- columnPrefix = "",
columnName = "isCommunalAvailable",
initialValue = false,
)
@@ -173,6 +182,33 @@ constructor(
replay = 1,
)
+ /**
+ * Whether communal hub should be shown automatically, depending on the user's [WhenToDream]
+ * state.
+ */
+ val shouldShowCommunal: Flow<Boolean> =
+ allOf(
+ isCommunalAvailable,
+ communalSettingsInteractor.whenToDream
+ .flatMapLatest { whenToDream ->
+ when (whenToDream) {
+ WhenToDream.NEVER -> flowOf(false)
+
+ WhenToDream.WHILE_CHARGING -> batteryInteractor.isDevicePluggedIn
+
+ WhenToDream.WHILE_DOCKED ->
+ allOf(
+ batteryInteractor.isDevicePluggedIn,
+ dockManager.retrieveIsDocked(),
+ )
+
+ WhenToDream.WHILE_POSTURED ->
+ allOf(batteryInteractor.isDevicePluggedIn, posturingInteractor.postured)
+ }
+ }
+ .flowOn(bgDispatcher),
+ )
+
private val _isDisclaimerDismissed = MutableStateFlow(false)
val isDisclaimerDismissed: Flow<Boolean> = _isDisclaimerDismissed.asStateFlow()
@@ -300,7 +336,6 @@ constructor(
}
.logDiffsForTable(
tableLogBuffer = tableLogBuffer,
- columnPrefix = "",
columnName = "isCommunalShowing",
initialValue = false,
)
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalPrefsInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalPrefsInteractor.kt
index ec45d6c8d545..cdf17033cc01 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalPrefsInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalPrefsInteractor.kt
@@ -49,7 +49,6 @@ constructor(
.flatMapLatest { user -> repository.isCtaDismissed(user) }
.logDiffsForTable(
tableLogBuffer = tableLogBuffer,
- columnPrefix = "",
columnName = "isCtaDismissed",
initialValue = false,
)
@@ -67,7 +66,6 @@ constructor(
.flatMapLatest { user -> repository.isHubOnboardingDismissed(user) }
.logDiffsForTable(
tableLogBuffer = tableLogBuffer,
- columnPrefix = "",
columnName = "isHubOnboardingDismissed",
initialValue = false,
)
@@ -80,6 +78,24 @@ constructor(
fun setHubOnboardingDismissed(user: UserInfo = userTracker.userInfo) =
bgScope.launch { repository.setHubOnboardingDismissed(user) }
+ val isDreamButtonTooltipDismissed: Flow<Boolean> =
+ userInteractor.selectedUserInfo
+ .flatMapLatest { user -> repository.isDreamButtonTooltipDismissed(user) }
+ .logDiffsForTable(
+ tableLogBuffer = tableLogBuffer,
+ columnPrefix = "",
+ columnName = "isDreamButtonTooltipDismissed",
+ initialValue = false,
+ )
+ .stateIn(
+ scope = bgScope,
+ started = SharingStarted.WhileSubscribed(),
+ initialValue = false,
+ )
+
+ fun setDreamButtonTooltipDismissed(user: UserInfo = userTracker.userInfo) =
+ bgScope.launch { repository.setDreamButtonTooltipDismissed(user) }
+
private companion object {
const val TAG = "CommunalPrefsInteractor"
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSettingsInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSettingsInteractor.kt
index 1738f37b7f0c..a0b1261df346 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSettingsInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSettingsInteractor.kt
@@ -21,6 +21,7 @@ import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCall
import com.android.systemui.communal.data.model.CommunalEnabledState
import com.android.systemui.communal.data.repository.CommunalSettingsRepository
import com.android.systemui.communal.shared.model.CommunalBackgroundType
+import com.android.systemui.communal.shared.model.WhenToDream
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.log.dagger.CommunalTableLog
@@ -73,6 +74,12 @@ constructor(
repository.getScreensaverEnabledState(user)
}
+ /** When to dream for the currently selected user. */
+ val whenToDream: Flow<WhenToDream> =
+ userInteractor.selectedUserInfo.flatMapLatest { user ->
+ repository.getWhenToDreamState(user)
+ }
+
/**
* Returns true if any glanceable hub functionality should be enabled via configs and flags.
*
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractor.kt
index 4e4ecc949d09..8f55d96e947a 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractor.kt
@@ -65,7 +65,6 @@ constructor(
}
.logDiffsForTable(
tableLogBuffer = tableLogBuffer,
- columnPrefix = "",
columnName = "isTutorialAvailable",
initialValue = false,
)
diff --git a/packages/SystemUI/src/com/android/systemui/communal/shared/model/WhenToDream.kt b/packages/SystemUI/src/com/android/systemui/communal/shared/model/WhenToDream.kt
new file mode 100644
index 000000000000..0d4eb60c5240
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/shared/model/WhenToDream.kt
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.communal.shared.model
+
+enum class WhenToDream {
+ NEVER,
+ WHILE_CHARGING,
+ WHILE_DOCKED,
+ WHILE_POSTURED,
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalToDreamButtonViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalToDreamButtonViewModel.kt
index bbb168680221..7e683c45e525 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalToDreamButtonViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalToDreamButtonViewModel.kt
@@ -20,11 +20,14 @@ import android.annotation.SuppressLint
import android.app.DreamManager
import android.content.Intent
import android.provider.Settings
+import androidx.compose.runtime.getValue
import com.android.internal.logging.UiEventLogger
+import com.android.systemui.communal.domain.interactor.CommunalPrefsInteractor
import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor
import com.android.systemui.communal.shared.log.CommunalUiEvent
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.lifecycle.ExclusiveActivatable
+import com.android.systemui.lifecycle.Hydrator
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.statusbar.policy.BatteryController
import com.android.systemui.util.kotlin.isDevicePluggedIn
@@ -37,7 +40,7 @@ import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.distinctUntilChanged
-import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.receiveAsFlow
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
@@ -47,18 +50,36 @@ class CommunalToDreamButtonViewModel
constructor(
@Background private val backgroundContext: CoroutineContext,
batteryController: BatteryController,
+ private val prefsInteractor: CommunalPrefsInteractor,
private val settingsInteractor: CommunalSettingsInteractor,
private val activityStarter: ActivityStarter,
private val dreamManager: DreamManager,
private val uiEventLogger: UiEventLogger,
) : ExclusiveActivatable() {
+ private val hydrator = Hydrator("CommunalToDreamButtonViewModel.hydrator")
private val _requests = Channel<Unit>(Channel.BUFFERED)
/** Whether we should show a button on hub to switch to dream. */
- @SuppressLint("MissingPermission")
- val shouldShowDreamButtonOnHub =
- batteryController.isDevicePluggedIn().distinctUntilChanged().flowOn(backgroundContext)
+ val shouldShowDreamButtonOnHub: Boolean by
+ hydrator.hydratedStateOf(
+ traceName = "shouldShowDreamButtonOnHub",
+ initialValue = false,
+ source = batteryController.isDevicePluggedIn().distinctUntilChanged(),
+ )
+
+ /** Return whether the dream button tooltip has been dismissed. */
+ val shouldShowTooltip: Boolean by
+ hydrator.hydratedStateOf(
+ traceName = "shouldShowTooltip",
+ initialValue = false,
+ source = prefsInteractor.isDreamButtonTooltipDismissed.map { !it },
+ )
+
+ /** Set the dream button tooltip to be dismissed. */
+ fun setDreamButtonTooltipDismissed() {
+ prefsInteractor.setDreamButtonTooltipDismissed()
+ }
/** Handle a tap on the "show dream" button. */
fun onShowDreamButtonTap() {
@@ -86,6 +107,8 @@ constructor(
}
}
+ launch { hydrator.activate() }
+
awaitCancellation()
}
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryBiometricAuthInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryBiometricAuthInteractor.kt
index 1e7bec257432..69da67e055fe 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryBiometricAuthInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryBiometricAuthInteractor.kt
@@ -68,4 +68,10 @@ constructor(
emptyFlow()
}
}
+
+ /** Triggered if a face failure occurs regardless of the mode. */
+ val faceFailure: Flow<FailedFaceAuthenticationStatus> =
+ deviceEntryFaceAuthInteractor.authenticationStatus.filterIsInstance<
+ FailedFaceAuthenticationStatus
+ >()
}
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractor.kt
index cdd2b054711e..079d624e6fe0 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractor.kt
@@ -19,6 +19,7 @@ package com.android.systemui.deviceentry.domain.interactor
import com.android.systemui.CoreStartable
import com.android.systemui.deviceentry.shared.model.FaceAuthenticationStatus
import com.android.systemui.deviceentry.shared.model.FaceDetectionStatus
+import com.android.systemui.log.table.TableLogBuffer
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.StateFlow
@@ -81,6 +82,8 @@ interface DeviceEntryFaceAuthInteractor : CoreStartable {
/** Whether face auth is considered class 3 */
fun isFaceAuthStrong(): Boolean
+
+ suspend fun hydrateTableLogBuffer(tableLogBuffer: TableLogBuffer)
}
/**
@@ -93,17 +96,17 @@ interface DeviceEntryFaceAuthInteractor : CoreStartable {
*/
interface FaceAuthenticationListener {
/** Receive face isAuthenticated updates */
- fun onAuthenticatedChanged(isAuthenticated: Boolean)
+ fun onAuthenticatedChanged(isAuthenticated: Boolean) = Unit
/** Receive face authentication status updates */
- fun onAuthenticationStatusChanged(status: FaceAuthenticationStatus)
+ fun onAuthenticationStatusChanged(status: FaceAuthenticationStatus) = Unit
/** Receive status updates whenever face detection runs */
- fun onDetectionStatusChanged(status: FaceDetectionStatus)
+ fun onDetectionStatusChanged(status: FaceDetectionStatus) = Unit
- fun onLockoutStateChanged(isLockedOut: Boolean)
+ fun onLockoutStateChanged(isLockedOut: Boolean) = Unit
- fun onRunningStateChanged(isRunning: Boolean)
+ fun onRunningStateChanged(isRunning: Boolean) = Unit
- fun onAuthEnrollmentStateChanged(enrolled: Boolean)
+ fun onAuthEnrollmentStateChanged(enrolled: Boolean) = Unit
}
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryHapticsInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryHapticsInteractor.kt
index cd456a618c48..38e0503440f9 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryHapticsInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryHapticsInteractor.kt
@@ -123,7 +123,7 @@ constructor(
private val playErrorHapticForBiometricFailure: Flow<Unit> =
merge(
deviceEntryFingerprintAuthInteractor.fingerprintFailure,
- deviceEntryBiometricAuthInteractor.faceOnlyFaceFailure,
+ deviceEntryBiometricAuthInteractor.faceFailure,
)
// map to Unit
.map {}
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt
index 4ddc98cd434f..5b6859761705 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt
@@ -25,7 +25,10 @@ import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.deviceentry.data.repository.DeviceEntryRepository
import com.android.systemui.keyguard.DismissCallbackRegistry
+import com.android.systemui.log.table.TableLogBuffer
+import com.android.systemui.log.table.logDiffsForTable
import com.android.systemui.scene.data.model.asIterable
+import com.android.systemui.scene.domain.SceneFrameworkTableLog
import com.android.systemui.scene.domain.interactor.SceneBackInteractor
import com.android.systemui.scene.domain.interactor.SceneInteractor
import com.android.systemui.scene.shared.model.Scenes
@@ -33,9 +36,11 @@ import com.android.systemui.util.kotlin.pairwise
import com.android.systemui.utils.coroutines.flow.mapLatestConflated
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.filter
@@ -43,6 +48,7 @@ import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.launch
/**
* Hosts application business logic related to device entry.
@@ -62,6 +68,7 @@ constructor(
private val alternateBouncerInteractor: AlternateBouncerInteractor,
private val dismissCallbackRegistry: DismissCallbackRegistry,
sceneBackInteractor: SceneBackInteractor,
+ @SceneFrameworkTableLog private val tableLogBuffer: TableLogBuffer,
) {
/**
* Whether the device is unlocked.
@@ -147,6 +154,11 @@ constructor(
) { enteredDirectly, enteredOnBackStack ->
enteredOnBackStack || enteredDirectly
}
+ .logDiffsForTable(
+ tableLogBuffer = tableLogBuffer,
+ columnName = "isDeviceEntered",
+ initialValue = false,
+ )
.stateIn(
scope = applicationScope,
started = SharingStarted.Eagerly,
@@ -184,6 +196,11 @@ constructor(
deviceUnlockStatus.deviceUnlockSource?.dismissesLockscreen == false)) &&
!isDeviceEntered
}
+ .logDiffsForTable(
+ tableLogBuffer = tableLogBuffer,
+ columnName = "canSwipeToEnter",
+ initialValue = false,
+ )
.stateIn(
scope = applicationScope,
started = SharingStarted.Eagerly,
@@ -271,4 +288,29 @@ constructor(
fun lockNow() {
deviceUnlockedInteractor.lockNow()
}
+
+ suspend fun hydrateTableLogBuffer(tableLogBuffer: TableLogBuffer) {
+ coroutineScope {
+ launch {
+ isDeviceEntered
+ .logDiffsForTable(
+ tableLogBuffer = tableLogBuffer,
+ columnName = "isDeviceEntered",
+ initialValue = isDeviceEntered.value,
+ )
+ .collect()
+ }
+
+ launch {
+ canSwipeToEnter
+ .map { it?.toString() ?: "" }
+ .logDiffsForTable(
+ tableLogBuffer = tableLogBuffer,
+ columnName = "canSwipeToEnter",
+ initialValue = canSwipeToEnter.value?.toString() ?: "",
+ )
+ .collect()
+ }
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceUnlockedInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceUnlockedInteractor.kt
index 68aef521be7b..b1be9a209a0a 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceUnlockedInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceUnlockedInteractor.kt
@@ -33,8 +33,11 @@ import com.android.systemui.keyguard.KeyguardViewMediator
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.domain.interactor.TrustInteractor
import com.android.systemui.lifecycle.ExclusiveActivatable
+import com.android.systemui.log.table.TableLogBuffer
+import com.android.systemui.log.table.logDiffsForTable
import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.power.shared.model.WakeSleepReason
+import com.android.systemui.scene.domain.SceneFrameworkTableLog
import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.util.settings.repository.UserAwareSecureSettingsRepository
import com.android.systemui.utils.coroutines.flow.flatMapLatestConflated
@@ -48,6 +51,7 @@ import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
@@ -74,6 +78,7 @@ constructor(
private val systemPropertiesHelper: SystemPropertiesHelper,
private val userAwareSecureSettingsRepository: UserAwareSecureSettingsRepository,
private val keyguardInteractor: KeyguardInteractor,
+ @SceneFrameworkTableLog private val tableLogBuffer: TableLogBuffer,
) : ExclusiveActivatable() {
private val deviceUnlockSource =
@@ -179,17 +184,33 @@ constructor(
private val lockNowRequests = Channel<Unit>()
override suspend fun onActivated(): Nothing {
- authenticationInteractor.authenticationMethod.collectLatest { authMethod ->
- if (!authMethod.isSecure) {
- // Device remains unlocked as long as the authentication method is not secure.
- Log.d(TAG, "remaining unlocked because auth method not secure")
- repository.deviceUnlockStatus.value = DeviceUnlockStatus(true, null)
- } else if (authMethod == AuthenticationMethodModel.Sim) {
- // Device remains locked while SIM is locked.
- Log.d(TAG, "remaining locked because SIM locked")
- repository.deviceUnlockStatus.value = DeviceUnlockStatus(false, null)
- } else {
- handleLockAndUnlockEvents()
+ coroutineScope {
+ launch {
+ authenticationInteractor.authenticationMethod.collectLatest { authMethod ->
+ if (!authMethod.isSecure) {
+ // Device remains unlocked as long as the authentication method is not
+ // secure.
+ Log.d(TAG, "remaining unlocked because auth method not secure")
+ repository.deviceUnlockStatus.value = DeviceUnlockStatus(true, null)
+ } else if (authMethod == AuthenticationMethodModel.Sim) {
+ // Device remains locked while SIM is locked.
+ Log.d(TAG, "remaining locked because SIM locked")
+ repository.deviceUnlockStatus.value = DeviceUnlockStatus(false, null)
+ } else {
+ handleLockAndUnlockEvents()
+ }
+ }
+ }
+
+ launch {
+ deviceUnlockStatus
+ .map { it.isUnlocked }
+ .logDiffsForTable(
+ tableLogBuffer = tableLogBuffer,
+ columnName = "isUnlocked",
+ initialValue = deviceUnlockStatus.value.isUnlocked,
+ )
+ .collect()
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/NoopDeviceEntryFaceAuthInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/NoopDeviceEntryFaceAuthInteractor.kt
index 9b8c2b1acc33..ecc4dbc2326a 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/NoopDeviceEntryFaceAuthInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/NoopDeviceEntryFaceAuthInteractor.kt
@@ -19,6 +19,7 @@ package com.android.systemui.deviceentry.domain.interactor
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.deviceentry.shared.model.FaceAuthenticationStatus
import com.android.systemui.deviceentry.shared.model.FaceDetectionStatus
+import com.android.systemui.log.table.TableLogBuffer
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
@@ -73,4 +74,6 @@ class NoopDeviceEntryFaceAuthInteractor @Inject constructor() : DeviceEntryFaceA
override fun onWalletLaunched() = Unit
override fun onDeviceUnfolded() {}
+
+ override suspend fun hydrateTableLogBuffer(tableLogBuffer: TableLogBuffer) {}
}
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/SystemUIDeviceEntryFaceAuthInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/SystemUIDeviceEntryFaceAuthInteractor.kt
index b19b2d9ece02..4b90e1d52ea0 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/SystemUIDeviceEntryFaceAuthInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/SystemUIDeviceEntryFaceAuthInteractor.kt
@@ -44,6 +44,8 @@ import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
import com.android.systemui.keyguard.shared.model.KeyguardState.OFF
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.log.FaceAuthenticationLogger
+import com.android.systemui.log.table.TableLogBuffer
+import com.android.systemui.log.table.logDiffsForTable
import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.res.R
import com.android.systemui.scene.domain.interactor.SceneInteractor
@@ -53,13 +55,16 @@ import com.android.systemui.user.data.model.SelectionStatus
import com.android.systemui.user.data.repository.UserRepository
import com.android.systemui.util.kotlin.pairwise
import com.android.systemui.util.kotlin.sample
+import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
import dagger.Lazy
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.filterNotNull
@@ -379,6 +384,27 @@ constructor(
.launchIn(applicationScope)
}
+ override suspend fun hydrateTableLogBuffer(tableLogBuffer: TableLogBuffer) {
+ conflatedCallbackFlow {
+ val listener =
+ object : FaceAuthenticationListener {
+ override fun onAuthEnrollmentStateChanged(enrolled: Boolean) {
+ trySend(isFaceAuthEnabledAndEnrolled())
+ }
+ }
+
+ registerListener(listener)
+
+ awaitClose { unregisterListener(listener) }
+ }
+ .logDiffsForTable(
+ tableLogBuffer = tableLogBuffer,
+ columnName = "isFaceAuthEnabledAndEnrolled",
+ initialValue = isFaceAuthEnabledAndEnrolled(),
+ )
+ .collect()
+ }
+
companion object {
const val TAG = "DeviceEntryFaceAuthInteractor"
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
index 8c6037107c5a..cf712f111034 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
@@ -19,8 +19,12 @@ package com.android.systemui.keyguard.domain.interactor
import android.animation.ValueAnimator
import android.util.MathUtils
import com.android.app.animation.Interpolators
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.systemui.Flags.communalSceneKtfRefactor
+import com.android.systemui.communal.domain.interactor.CommunalInteractor
+import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor
import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor
+import com.android.systemui.communal.shared.model.CommunalScenes
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
@@ -39,6 +43,10 @@ import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.shade.data.repository.ShadeRepository
import com.android.systemui.util.kotlin.sample
+import java.util.UUID
+import javax.inject.Inject
+import kotlin.time.Duration.Companion.milliseconds
+import kotlin.time.Duration.Companion.seconds
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
@@ -47,11 +55,6 @@ import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onStart
-import kotlin.time.Duration.Companion.milliseconds
-import kotlin.time.Duration.Companion.seconds
-import java.util.UUID
-import javax.inject.Inject
-import com.android.app.tracing.coroutines.launchTraced as launch
@SysUISingleton
class FromLockscreenTransitionInteractor
@@ -68,6 +71,8 @@ constructor(
powerInteractor: PowerInteractor,
private val glanceableHubTransitions: GlanceableHubTransitions,
private val communalSettingsInteractor: CommunalSettingsInteractor,
+ private val communalInteractor: CommunalInteractor,
+ private val communalSceneInteractor: CommunalSceneInteractor,
private val swipeToDismissInteractor: SwipeToDismissInteractor,
keyguardOcclusionInteractor: KeyguardOcclusionInteractor,
) :
@@ -94,6 +99,9 @@ constructor(
if (!communalSceneKtfRefactor()) {
listenForLockscreenToGlanceableHub()
}
+ if (communalSettingsInteractor.isV2FlagEnabled()) {
+ listenForLockscreenToGlanceableHubV2()
+ }
}
/**
@@ -268,9 +276,7 @@ constructor(
it.transitionState == TransitionState.CANCELED &&
it.to == KeyguardState.PRIMARY_BOUNCER
}
- .collect {
- transitionId = null
- }
+ .collect { transitionId = null }
}
}
@@ -370,6 +376,19 @@ constructor(
}
}
+ private fun listenForLockscreenToGlanceableHubV2() {
+ scope.launch {
+ communalInteractor.shouldShowCommunal
+ .filterRelevantKeyguardStateAnd { shouldShow -> shouldShow }
+ .collect {
+ communalSceneInteractor.changeScene(
+ newScene = CommunalScenes.Communal,
+ loggingReason = "lockscreen to communal",
+ )
+ }
+ }
+ }
+
override fun getDefaultAnimatorForTransitionsToState(toState: KeyguardState): ValueAnimator {
return ValueAnimator().apply {
interpolator = Interpolators.LINEAR
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardEnabledInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardEnabledInteractor.kt
index 42cbd7d39248..a1f288edcdd3 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardEnabledInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardEnabledInteractor.kt
@@ -24,6 +24,8 @@ import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository
import com.android.systemui.keyguard.data.repository.KeyguardRepository
import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.log.table.TableLogBuffer
+import com.android.systemui.log.table.logDiffsForTable
import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import com.android.systemui.util.kotlin.sample
@@ -32,6 +34,7 @@ import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
@@ -166,4 +169,14 @@ constructor(
isKeyguardEnabled.value && lockPatternUtils.isLockScreenDisabled(userId)
}
}
+
+ suspend fun hydrateTableLogBuffer(tableLogBuffer: TableLogBuffer) {
+ isKeyguardEnabled
+ .logDiffsForTable(
+ tableLogBuffer = tableLogBuffer,
+ columnName = "isKeyguardEnabled",
+ initialValue = isKeyguardEnabled.value,
+ )
+ .collect()
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
index 75178f0ffef0..3739d17da6c4 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
@@ -42,6 +42,8 @@ import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
import com.android.systemui.keyguard.shared.model.KeyguardState.OCCLUDED
import com.android.systemui.keyguard.shared.model.StatusBarState
+import com.android.systemui.log.table.TableLogBuffer
+import com.android.systemui.log.table.logDiffsForTable
import com.android.systemui.res.R
import com.android.systemui.scene.domain.interactor.SceneInteractor
import com.android.systemui.scene.shared.flag.SceneContainerFlag
@@ -60,6 +62,7 @@ import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.combineTransform
import kotlinx.coroutines.flow.debounce
@@ -533,6 +536,16 @@ constructor(
repository.setNotificationStackAbsoluteBottom(bottom)
}
+ suspend fun hydrateTableLogBuffer(tableLogBuffer: TableLogBuffer) {
+ isDozing
+ .logDiffsForTable(
+ tableLogBuffer = tableLogBuffer,
+ columnName = "isDozing",
+ initialValue = isDozing.value,
+ )
+ .collect()
+ }
+
companion object {
private const val TAG = "KeyguardInteractor"
/**
diff --git a/packages/SystemUI/src/com/android/systemui/power/domain/interactor/PowerInteractor.kt b/packages/SystemUI/src/com/android/systemui/power/domain/interactor/PowerInteractor.kt
index f15a7b30dce7..f8d442de0f55 100644
--- a/packages/SystemUI/src/com/android/systemui/power/domain/interactor/PowerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/power/domain/interactor/PowerInteractor.kt
@@ -22,6 +22,8 @@ import com.android.systemui.camera.CameraGestureHelper
import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.classifier.FalsingCollectorActual
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.log.table.TableLogBuffer
+import com.android.systemui.log.table.logDiffsForTable
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.power.data.repository.PowerRepository
import com.android.systemui.power.shared.model.DozeScreenStateModel
@@ -35,6 +37,7 @@ import javax.inject.Provider
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
@@ -228,6 +231,15 @@ constructor(
repository.updateWakefulness(powerButtonLaunchGestureTriggered = true)
}
+ suspend fun hydrateTableLogBuffer(tableLogBuffer: TableLogBuffer) {
+ detailedWakefulness
+ .logDiffsForTable(
+ tableLogBuffer = tableLogBuffer,
+ initialValue = detailedWakefulness.value,
+ )
+ .collect()
+ }
+
companion object {
private const val FSI_WAKE_WHY = "full_screen_intent"
diff --git a/packages/SystemUI/src/com/android/systemui/power/shared/model/WakefulnessModel.kt b/packages/SystemUI/src/com/android/systemui/power/shared/model/WakefulnessModel.kt
index 0f49c94c3195..297c6af5a4a7 100644
--- a/packages/SystemUI/src/com/android/systemui/power/shared/model/WakefulnessModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/power/shared/model/WakefulnessModel.kt
@@ -1,6 +1,8 @@
package com.android.systemui.power.shared.model
import com.android.systemui.keyguard.KeyguardService
+import com.android.systemui.log.table.Diffable
+import com.android.systemui.log.table.TableRowLogger
/**
* Models whether the device is awake or asleep, along with information about why we're in that
@@ -35,7 +37,7 @@ data class WakefulnessModel(
* to a subsequent power gesture.
*/
val powerButtonLaunchGestureTriggered: Boolean = false,
-) {
+) : Diffable<WakefulnessModel> {
fun isAwake() =
internalWakefulnessState == WakefulnessState.AWAKE ||
internalWakefulnessState == WakefulnessState.STARTING_TO_WAKE
@@ -58,4 +60,8 @@ data class WakefulnessModel(
return isAwake() &&
(lastWakeReason == WakeSleepReason.TAP || lastWakeReason == WakeSleepReason.GESTURE)
}
+
+ override fun logDiffs(prevVal: WakefulnessModel, row: TableRowLogger) {
+ row.logChange(columnName = "wakefulness", value = toString())
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/SceneDomainModule.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/SceneDomainModule.kt
index be792df340c9..f2f237ac987e 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/SceneDomainModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/SceneDomainModule.kt
@@ -16,13 +16,27 @@
package com.android.systemui.scene.domain
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.log.table.TableLogBuffer
+import com.android.systemui.log.table.TableLogBufferFactory
import com.android.systemui.scene.domain.resolver.SceneResolverModule
import dagger.Module
+import dagger.Provides
+import javax.inject.Qualifier
-@Module(
- includes =
- [
- SceneResolverModule::class,
- ]
-)
-object SceneDomainModule
+@Module(includes = [SceneResolverModule::class])
+object SceneDomainModule {
+
+ @JvmStatic
+ @Provides
+ @SysUISingleton
+ @SceneFrameworkTableLog
+ fun provideSceneFrameworkTableLogBuffer(factory: TableLogBufferFactory): TableLogBuffer {
+ return factory.create("SceneFrameworkTableLog", 100)
+ }
+}
+
+@Qualifier
+@MustBeDocumented
+@Retention(AnnotationRetention.RUNTIME)
+annotation class SceneFrameworkTableLog
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneBackInteractor.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneBackInteractor.kt
index bebd398ac972..c9d8e0244d20 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneBackInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneBackInteractor.kt
@@ -18,11 +18,16 @@ package com.android.systemui.scene.domain.interactor
import com.android.compose.animation.scene.SceneKey
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.log.table.Diffable
+import com.android.systemui.log.table.TableLogBuffer
+import com.android.systemui.log.table.TableRowLogger
import com.android.systemui.scene.data.model.SceneStack
+import com.android.systemui.scene.data.model.asIterable
import com.android.systemui.scene.data.model.peek
import com.android.systemui.scene.data.model.pop
import com.android.systemui.scene.data.model.push
import com.android.systemui.scene.data.model.sceneStackOf
+import com.android.systemui.scene.domain.SceneFrameworkTableLog
import com.android.systemui.scene.shared.logger.SceneLogger
import com.android.systemui.scene.shared.model.SceneContainerConfig
import javax.inject.Inject
@@ -39,6 +44,7 @@ class SceneBackInteractor
constructor(
private val logger: SceneLogger,
private val sceneContainerConfig: SceneContainerConfig,
+ @SceneFrameworkTableLog private val tableLogBuffer: TableLogBuffer,
) {
private val _backStack = MutableStateFlow(sceneStackOf())
val backStack: StateFlow<SceneStack> = _backStack.asStateFlow()
@@ -58,6 +64,7 @@ constructor(
fun onSceneChange(from: SceneKey, to: SceneKey) {
check(from != to) { "from == to, from=${from.debugName}, to=${to.debugName}" }
+ val prevVal = backStack.value
_backStack.update { stack ->
when (stackOperation(from, to, stack)) {
null -> stack
@@ -68,12 +75,21 @@ constructor(
}
}
logger.logSceneBackStack(backStack.value)
+ tableLogBuffer.logDiffs(
+ prevVal = DiffableSceneStack(prevVal),
+ newVal = DiffableSceneStack(backStack.value),
+ )
}
/** Applies the given [transform] to the back stack. */
fun updateBackStack(transform: (SceneStack) -> SceneStack) {
+ val prevVal = backStack.value
_backStack.update { stack -> transform(stack) }
logger.logSceneBackStack(backStack.value)
+ tableLogBuffer.logDiffs(
+ prevVal = DiffableSceneStack(prevVal),
+ newVal = DiffableSceneStack(backStack.value),
+ )
}
private fun stackOperation(from: SceneKey, to: SceneKey, stack: SceneStack): StackOperation? {
@@ -106,4 +122,15 @@ constructor(
private data object Push : StackOperation
private data object Pop : StackOperation
+
+ private class DiffableSceneStack(private val sceneStack: SceneStack) :
+ Diffable<DiffableSceneStack> {
+
+ override fun logDiffs(prevVal: DiffableSceneStack, row: TableRowLogger) {
+ row.logChange(
+ columnName = "backStack",
+ value = sceneStack.asIterable().joinToString { it.debugName },
+ )
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt
index 8bc9d96c064a..9c04323f2a0e 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt
@@ -27,14 +27,19 @@ import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.deviceentry.domain.interactor.DeviceUnlockedInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardEnabledInteractor
+import com.android.systemui.log.table.Diffable
+import com.android.systemui.log.table.TableLogBuffer
+import com.android.systemui.log.table.TableRowLogger
import com.android.systemui.scene.data.repository.SceneContainerRepository
import com.android.systemui.scene.domain.resolver.SceneResolver
import com.android.systemui.scene.shared.logger.SceneLogger
import com.android.systemui.scene.shared.model.SceneFamilies
import com.android.systemui.scene.shared.model.Scenes
+import com.android.systemui.util.kotlin.pairwise
import dagger.Lazy
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
@@ -47,6 +52,7 @@ import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.flow.update
+import kotlinx.coroutines.launch
/**
* Generic business logic and app state accessors for the scene framework.
@@ -562,6 +568,28 @@ constructor(
decrementActiveTransitionAnimationCount()
}
+ suspend fun hydrateTableLogBuffer(tableLogBuffer: TableLogBuffer) {
+ coroutineScope {
+ launch {
+ currentScene
+ .map { sceneKey -> DiffableSceneKey(key = sceneKey) }
+ .pairwise()
+ .collect { (prev, current) ->
+ tableLogBuffer.logDiffs(prevVal = prev, newVal = current)
+ }
+ }
+
+ launch {
+ currentOverlays
+ .map { overlayKeys -> DiffableOverlayKeys(keys = overlayKeys) }
+ .pairwise()
+ .collect { (prev, current) ->
+ tableLogBuffer.logDiffs(prevVal = prev, newVal = current)
+ }
+ }
+ }
+ }
+
private fun decrementActiveTransitionAnimationCount() {
repository.activeTransitionAnimationCount.update { current ->
(current - 1).also {
@@ -573,4 +601,20 @@ constructor(
}
}
}
+
+ private class DiffableSceneKey(private val key: SceneKey) : Diffable<DiffableSceneKey> {
+ override fun logDiffs(prevVal: DiffableSceneKey, row: TableRowLogger) {
+ row.logChange(columnName = "currentScene", value = key.debugName)
+ }
+ }
+
+ private class DiffableOverlayKeys(private val keys: Set<OverlayKey>) :
+ Diffable<DiffableOverlayKeys> {
+ override fun logDiffs(prevVal: DiffableOverlayKeys, row: TableRowLogger) {
+ row.logChange(
+ columnName = "currentOverlays",
+ value = keys.joinToString { key -> key.debugName },
+ )
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
index 8602884ec4ee..2fd584176220 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
@@ -45,6 +45,7 @@ import com.android.systemui.keyguard.DismissCallbackRegistry
import com.android.systemui.keyguard.domain.interactor.KeyguardEnabledInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.domain.interactor.WindowManagerLockscreenVisibilityInteractor.Companion.keyguardScenes
+import com.android.systemui.log.table.TableLogBuffer
import com.android.systemui.model.SceneContainerPlugin
import com.android.systemui.model.SysUiState
import com.android.systemui.model.updateFlags
@@ -54,6 +55,7 @@ import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.power.shared.model.WakeSleepReason
import com.android.systemui.scene.data.model.asIterable
import com.android.systemui.scene.data.model.sceneStackOf
+import com.android.systemui.scene.domain.SceneFrameworkTableLog
import com.android.systemui.scene.domain.interactor.DisabledContentInteractor
import com.android.systemui.scene.domain.interactor.SceneBackInteractor
import com.android.systemui.scene.domain.interactor.SceneContainerOcclusionInteractor
@@ -145,6 +147,7 @@ constructor(
private val disabledContentInteractor: DisabledContentInteractor,
private val activityTransitionAnimator: ActivityTransitionAnimator,
private val shadeModeInteractor: ShadeModeInteractor,
+ @SceneFrameworkTableLog private val tableLogBuffer: TableLogBuffer,
) : CoreStartable {
private val centralSurfaces: CentralSurfaces?
get() = centralSurfacesOptLazy.get().getOrNull()
@@ -154,6 +157,7 @@ constructor(
override fun start() {
if (SceneContainerFlag.isEnabled) {
sceneLogger.logFrameworkEnabled(isEnabled = true)
+ applicationScope.launch { hydrateTableLogBuffer() }
hydrateVisibility()
automaticallySwitchScenes()
hydrateSystemUiState()
@@ -224,6 +228,16 @@ constructor(
}
}
+ private suspend fun hydrateTableLogBuffer() {
+ coroutineScope {
+ launch { sceneInteractor.hydrateTableLogBuffer(tableLogBuffer) }
+ launch { keyguardEnabledInteractor.hydrateTableLogBuffer(tableLogBuffer) }
+ launch { faceUnlockInteractor.hydrateTableLogBuffer(tableLogBuffer) }
+ launch { powerInteractor.hydrateTableLogBuffer(tableLogBuffer) }
+ launch { keyguardInteractor.hydrateTableLogBuffer(tableLogBuffer) }
+ }
+ }
+
private fun resetShadeSessions() {
applicationScope.launch {
combine(
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeModeInteractor.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeModeInteractor.kt
index 59d812403777..01451502b859 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeModeInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeModeInteractor.kt
@@ -19,6 +19,9 @@ package com.android.systemui.shade.domain.interactor
import android.provider.Settings
import androidx.annotation.FloatRange
import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.log.table.TableLogBuffer
+import com.android.systemui.log.table.logDiffsForTable
+import com.android.systemui.scene.domain.SceneFrameworkTableLog
import com.android.systemui.shade.data.repository.ShadeRepository
import com.android.systemui.shade.shared.flag.DualShade
import com.android.systemui.shade.shared.model.ShadeMode
@@ -81,8 +84,9 @@ class ShadeModeInteractorImpl
@Inject
constructor(
@Application applicationScope: CoroutineScope,
- repository: ShadeRepository,
+ private val repository: ShadeRepository,
secureSettingsRepository: SecureSettingsRepository,
+ @SceneFrameworkTableLog private val tableLogBuffer: TableLogBuffer,
) : ShadeModeInteractor {
private val isDualShadeEnabled: Flow<Boolean> =
@@ -93,17 +97,17 @@ constructor(
override val isShadeLayoutWide: StateFlow<Boolean> = repository.isShadeLayoutWide
+ private val shadeModeInitialValue: ShadeMode
+ get() =
+ determineShadeMode(
+ isDualShadeEnabled = DUAL_SHADE_ENABLED_DEFAULT,
+ isShadeLayoutWide = repository.isShadeLayoutWide.value,
+ )
+
override val shadeMode: StateFlow<ShadeMode> =
combine(isDualShadeEnabled, repository.isShadeLayoutWide, ::determineShadeMode)
- .stateIn(
- applicationScope,
- SharingStarted.Eagerly,
- initialValue =
- determineShadeMode(
- isDualShadeEnabled = DUAL_SHADE_ENABLED_DEFAULT,
- isShadeLayoutWide = repository.isShadeLayoutWide.value,
- ),
- )
+ .logDiffsForTable(tableLogBuffer = tableLogBuffer, initialValue = shadeModeInitialValue)
+ .stateIn(applicationScope, SharingStarted.Eagerly, initialValue = shadeModeInitialValue)
@FloatRange(from = 0.0, to = 1.0) override fun getTopEdgeSplitFraction(): Float = 0.5f
diff --git a/packages/SystemUI/src/com/android/systemui/shade/shared/model/ShadeMode.kt b/packages/SystemUI/src/com/android/systemui/shade/shared/model/ShadeMode.kt
index a8199a402ef1..8b3ce0f69742 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/shared/model/ShadeMode.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/shared/model/ShadeMode.kt
@@ -16,15 +16,18 @@
package com.android.systemui.shade.shared.model
+import com.android.systemui.log.table.Diffable
+import com.android.systemui.log.table.TableRowLogger
+
/** Enumerates all known modes of operation of the shade. */
-sealed interface ShadeMode {
+sealed class ShadeMode : Diffable<ShadeMode> {
/**
* The single or "accordion" shade where the QS and notification parts are in two vertically
* stacked panels and the user can swipe up and down to expand or collapse between the two
* parts.
*/
- data object Single : ShadeMode
+ data object Single : ShadeMode()
/**
* The split shade where, on large screens and unfolded foldables, the QS and notification parts
@@ -32,14 +35,18 @@ sealed interface ShadeMode {
*
* Note: This isn't the only mode where the shade is wide.
*/
- data object Split : ShadeMode
+ data object Split : ShadeMode()
/**
* The dual shade where the QS and notification parts each have their own independently
* expandable/collapsible panel on either side of the large screen / unfolded device or sharing
* a space on a small screen or folded device.
*/
- data object Dual : ShadeMode
+ data object Dual : ShadeMode()
+
+ override fun logDiffs(prevVal: ShadeMode, row: TableRowLogger) {
+ row.logChange("shadeMode", toString())
+ }
companion object {
@JvmStatic fun dual(): Dual = Dual
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/binder/OngoingActivityChipBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/binder/OngoingActivityChipBinder.kt
index d46638fac46c..f5764d59e6ff 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/binder/OngoingActivityChipBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/binder/OngoingActivityChipBinder.kt
@@ -418,6 +418,7 @@ object OngoingActivityChipBinder {
}
is OngoingActivityChipModel.Shown.Timer,
is OngoingActivityChipModel.Shown.Text,
+ is OngoingActivityChipModel.Shown.ShortTimeDelta,
is OngoingActivityChipModel.Shown.IconOnly -> {
chipView.accessibilityLiveRegion = View.ACCESSIBILITY_LIVE_REGION_NONE
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/ChipContent.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/ChipContent.kt
index 13f4e51f2ba2..375e02989a3d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/ChipContent.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/ChipContent.kt
@@ -95,6 +95,10 @@ fun ChipContent(viewModel: OngoingActivityChipModel.Shown, modifier: Modifier =
is OngoingActivityChipModel.Shown.ShortTimeDelta -> {
// TODO(b/372657935): Implement ShortTimeDelta content in compose.
}
+
+ is OngoingActivityChipModel.Shown.IconOnly -> {
+ throw IllegalStateException("ChipContent should only be used if the chip shows text")
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/OngoingActivityChipModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/OngoingActivityChipModel.kt
index c6d6da2ad9aa..e0c764570132 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/OngoingActivityChipModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/OngoingActivityChipModel.kt
@@ -40,7 +40,7 @@ sealed class OngoingActivityChipModel {
}
/** This chip should be shown with the given information. */
- abstract class Shown(
+ sealed class Shown(
/** The icon to show on the chip. If null, no icon will be shown. */
open val icon: ChipIcon?,
/** What colors to use for the chip. */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinator.kt
index 2d1eccdf1abd..a0a86710b4ba 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinator.kt
@@ -22,6 +22,7 @@ import com.android.internal.widget.MessagingGroup
import com.android.internal.widget.MessagingMessage
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.keyguard.KeyguardUpdateMonitorCallback
+import com.android.systemui.Flags
import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.statusbar.NotificationLockscreenUserManager
import com.android.systemui.statusbar.NotificationLockscreenUserManager.UserChangedListener
@@ -144,7 +145,12 @@ internal constructor(
)
log { "ViewConfigCoordinator.updateNotificationsOnUiModeChanged()" }
traceSection("updateNotifOnUiModeChanged") {
- mPipeline?.allNotifs?.forEach { entry -> entry.row?.onUiModeChanged() }
+ mPipeline?.allNotifs?.forEach { entry ->
+ entry.row?.onUiModeChanged()
+ if (Flags.notificationUndoGutsOnConfigChanged()) {
+ mGutsManager.closeAndUndoGuts()
+ }
+ }
}
}
@@ -152,9 +158,15 @@ internal constructor(
colorUpdateLogger.logEvent("VCC.updateNotificationsOnDensityOrFontScaleChanged()")
mPipeline?.allNotifs?.forEach { entry ->
entry.onDensityOrFontScaleChanged()
- val exposedGuts = entry.areGutsExposed()
- if (exposedGuts) {
- mGutsManager.onDensityOrFontScaleChanged(entry)
+ if (Flags.notificationUndoGutsOnConfigChanged()) {
+ mGutsManager.closeAndUndoGuts()
+ } else {
+ // This property actually gets reset when the guts are re-inflated, so we're never
+ // actually calling onDensityOrFontScaleChanged below.
+ val exposedGuts = entry.areGutsExposed()
+ if (exposedGuts) {
+ mGutsManager.onDensityOrFontScaleChanged(entry)
+ }
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGuts.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGuts.java
index b86d1d934269..75d1c7c3d51e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGuts.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGuts.java
@@ -287,7 +287,7 @@ public class NotificationGuts extends FrameLayout {
* @param save whether the state should be saved
* @param force whether the guts should be force-closed regardless of state.
*/
- private void closeControls(int x, int y, boolean save, boolean force) {
+ public void closeControls(int x, int y, boolean save, boolean force) {
// First try to dismiss any blocking helper.
if (getWindowToken() == null) {
if (mClosedListener != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
index b1e5b22f9b1a..445cd010cd86 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
@@ -48,6 +48,7 @@ import com.android.internal.logging.nano.MetricsProto;
import com.android.internal.statusbar.IStatusBarService;
import com.android.settingslib.notification.ConversationIconFactory;
import com.android.systemui.CoreStartable;
+import com.android.systemui.Flags;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
@@ -223,6 +224,10 @@ public class NotificationGutsManager implements NotifGutsViewManager, CoreStarta
}
public void onDensityOrFontScaleChanged(NotificationEntry entry) {
+ if (!Flags.notificationUndoGutsOnConfigChanged()) {
+ Log.wtf(TAG, "onDensityOrFontScaleChanged should not be called if"
+ + " notificationUndoGutsOnConfigChanged is off");
+ }
setExposedGuts(entry.getGuts());
bindGuts(entry.getRow());
}
@@ -590,7 +595,8 @@ public class NotificationGutsManager implements NotifGutsViewManager, CoreStarta
}
/**
- * Closes guts or notification menus that might be visible and saves any changes.
+ * Closes guts or notification menus that might be visible and saves any changes if applicable
+ * (see {@link NotificationGuts.GutsContent#shouldBeSavedOnClose}).
*
* @param removeLeavebehinds true if leavebehinds (e.g. snooze) should be closed.
* @param force true if guts should be closed regardless of state (used for snooze only).
@@ -611,6 +617,20 @@ public class NotificationGutsManager implements NotifGutsViewManager, CoreStarta
}
/**
+ * Closes all guts that might be visible without saving changes.
+ */
+ public void closeAndUndoGuts() {
+ if (mNotificationGutsExposed != null) {
+ mNotificationGutsExposed.removeCallbacks(mOpenRunnable);
+ mNotificationGutsExposed.closeControls(
+ /* x = */ -1,
+ /* y = */ -1,
+ /* save = */ false,
+ /* force = */ false);
+ }
+ }
+
+ /**
* Returns the exposed NotificationGuts or null if none are exposed.
*/
public NotificationGuts getExposedGuts() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationSnooze.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationSnooze.java
index 99a6f6a59bd0..83897f5bc3a7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationSnooze.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationSnooze.java
@@ -51,6 +51,7 @@ import com.android.app.animation.Interpolators;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.systemui.Flags;
import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper.SnoozeOption;
import com.android.systemui.res.R;
@@ -86,18 +87,26 @@ public class NotificationSnooze extends LinearLayout
private NotificationSwipeActionHelper mSnoozeListener;
private StatusBarNotification mSbn;
- private View mSnoozeView;
- private TextView mSelectedOptionText;
+ @VisibleForTesting
+ public View mSnoozeView;
+ @VisibleForTesting
+ public TextView mSelectedOptionText;
private TextView mUndoButton;
- private ImageView mExpandButton;
- private View mDivider;
- private ViewGroup mSnoozeOptionContainer;
- private List<SnoozeOption> mSnoozeOptions;
+ @VisibleForTesting
+ public ImageView mExpandButton;
+ @VisibleForTesting
+ public View mDivider;
+ @VisibleForTesting
+ public ViewGroup mSnoozeOptionContainer;
+ @VisibleForTesting
+ public List<SnoozeOption> mSnoozeOptions;
private int mCollapsedHeight;
private SnoozeOption mDefaultOption;
- private SnoozeOption mSelectedOption;
+ @VisibleForTesting
+ public SnoozeOption mSelectedOption;
private boolean mSnoozing;
- private boolean mExpanded;
+ @VisibleForTesting
+ public boolean mExpanded;
private AnimatorSet mExpandAnimation;
private KeyValueListParser mParser;
@@ -334,7 +343,8 @@ public class NotificationSnooze extends LinearLayout
}
}
- private void showSnoozeOptions(boolean show) {
+ @VisibleForTesting
+ public void showSnoozeOptions(boolean show) {
int drawableId = show ? com.android.internal.R.drawable.ic_collapse_notification
: com.android.internal.R.drawable.ic_expand_notification;
mExpandButton.setImageResource(drawableId);
@@ -381,7 +391,8 @@ public class NotificationSnooze extends LinearLayout
mExpandAnimation.start();
}
- private void setSelected(SnoozeOption option, boolean userAction) {
+ @VisibleForTesting
+ public void setSelected(SnoozeOption option, boolean userAction) {
if (option != mSelectedOption) {
mSelectedOption = option;
mSelectedOptionText.setText(option.getConfirmation());
@@ -466,7 +477,12 @@ public class NotificationSnooze extends LinearLayout
@Override
public boolean handleCloseControls(boolean save, boolean force) {
- if (mExpanded && !force) {
+ if (Flags.notificationUndoGutsOnConfigChanged() && !save) {
+ // Undo changes and let the guts handle closing the view
+ mSelectedOption = null;
+ showSnoozeOptions(false);
+ return false;
+ } else if (mExpanded && !force) {
// Collapse expanded state on outside touch
showSnoozeOptions(false);
return true;
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/TutorialSelectionScreen.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/TutorialSelectionScreen.kt
index 9795cda97f37..eecea9228ea3 100644
--- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/TutorialSelectionScreen.kt
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/TutorialSelectionScreen.kt
@@ -27,6 +27,7 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.safeDrawingPadding
+import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
@@ -82,18 +83,29 @@ fun TutorialSelectionScreen(
}
),
) {
- val padding = if (hasCompactWindowSize()) 24.dp else 60.dp
+ val isCompactWindow = hasCompactWindowSize()
+ val padding = if (isCompactWindow) 24.dp else 60.dp
val configuration = LocalConfiguration.current
when (configuration.orientation) {
Configuration.ORIENTATION_LANDSCAPE -> {
- HorizontalSelectionButtons(
- onBackTutorialClicked = onBackTutorialClicked,
- onHomeTutorialClicked = onHomeTutorialClicked,
- onRecentAppsTutorialClicked = onRecentAppsTutorialClicked,
- onSwitchAppsTutorialClicked = onSwitchAppsTutorialClicked,
- modifier = Modifier.weight(1f).padding(padding),
- lastSelectedScreen,
- )
+ if (isCompactWindow)
+ HorizontalCompactSelectionButtons(
+ onBackTutorialClicked = onBackTutorialClicked,
+ onHomeTutorialClicked = onHomeTutorialClicked,
+ onRecentAppsTutorialClicked = onRecentAppsTutorialClicked,
+ onSwitchAppsTutorialClicked = onSwitchAppsTutorialClicked,
+ lastSelectedScreen,
+ modifier = Modifier.weight(1f).padding(padding),
+ )
+ else
+ HorizontalSelectionButtons(
+ onBackTutorialClicked = onBackTutorialClicked,
+ onHomeTutorialClicked = onHomeTutorialClicked,
+ onRecentAppsTutorialClicked = onRecentAppsTutorialClicked,
+ onSwitchAppsTutorialClicked = onSwitchAppsTutorialClicked,
+ lastSelectedScreen,
+ modifier = Modifier.weight(1f).padding(padding),
+ )
}
else -> {
VerticalSelectionButtons(
@@ -101,8 +113,8 @@ fun TutorialSelectionScreen(
onHomeTutorialClicked = onHomeTutorialClicked,
onRecentAppsTutorialClicked = onRecentAppsTutorialClicked,
onSwitchAppsTutorialClicked = onSwitchAppsTutorialClicked,
- modifier = Modifier.weight(1f).padding(padding),
lastSelectedScreen,
+ modifier = Modifier.weight(1f).padding(padding),
)
}
}
@@ -120,11 +132,99 @@ private fun HorizontalSelectionButtons(
onHomeTutorialClicked: () -> Unit,
onRecentAppsTutorialClicked: () -> Unit,
onSwitchAppsTutorialClicked: () -> Unit,
+ lastSelectedScreen: Screen,
modifier: Modifier = Modifier,
+) {
+ Column(modifier = modifier) {
+ TwoByTwoTutorialButtons(
+ onBackTutorialClicked,
+ onHomeTutorialClicked,
+ onRecentAppsTutorialClicked,
+ onSwitchAppsTutorialClicked,
+ lastSelectedScreen,
+ modifier = Modifier.weight(1f).fillMaxSize(),
+ )
+ }
+}
+
+@Composable
+private fun TwoByTwoTutorialButtons(
+ onBackTutorialClicked: () -> Unit,
+ onHomeTutorialClicked: () -> Unit,
+ onRecentAppsTutorialClicked: () -> Unit,
+ onSwitchAppsTutorialClicked: () -> Unit,
lastSelectedScreen: Screen,
+ modifier: Modifier = Modifier,
+) {
+ val homeFocusRequester = remember { FocusRequester() }
+ val backFocusRequester = remember { FocusRequester() }
+ val recentAppsFocusRequester = remember { FocusRequester() }
+ val switchAppsFocusRequester = remember { FocusRequester() }
+ LaunchedEffect(Unit) {
+ when (lastSelectedScreen) {
+ Screen.HOME_GESTURE -> homeFocusRequester.requestFocus()
+ Screen.BACK_GESTURE -> backFocusRequester.requestFocus()
+ Screen.RECENT_APPS_GESTURE -> recentAppsFocusRequester.requestFocus()
+ Screen.SWITCH_APPS_GESTURE -> switchAppsFocusRequester.requestFocus()
+ else -> {} // No-Op.
+ }
+ }
+ Column {
+ Row(Modifier.weight(1f)) {
+ TutorialButton(
+ text = stringResource(R.string.touchpad_tutorial_home_gesture_button),
+ icon = ImageVector.vectorResource(id = R.drawable.touchpad_tutorial_home_icon),
+ iconColor = MaterialTheme.colorScheme.onPrimary,
+ onClick = onHomeTutorialClicked,
+ backgroundColor = MaterialTheme.colorScheme.primary,
+ modifier = modifier.focusRequester(homeFocusRequester).focusable().fillMaxSize(),
+ )
+ Spacer(modifier = Modifier.size(16.dp))
+ TutorialButton(
+ text = stringResource(R.string.touchpad_tutorial_back_gesture_button),
+ icon = Icons.AutoMirrored.Outlined.ArrowBack,
+ iconColor = MaterialTheme.colorScheme.onTertiary,
+ onClick = onBackTutorialClicked,
+ backgroundColor = MaterialTheme.colorScheme.tertiary,
+ modifier = modifier.focusRequester(backFocusRequester).focusable().fillMaxSize(),
+ )
+ }
+ Spacer(modifier = Modifier.size(16.dp))
+ Row(Modifier.weight(1f)) {
+ TutorialButton(
+ text = stringResource(R.string.touchpad_tutorial_recent_apps_gesture_button),
+ icon = ImageVector.vectorResource(id = R.drawable.touchpad_tutorial_recents_icon),
+ iconColor = MaterialTheme.colorScheme.onSecondary,
+ onClick = onRecentAppsTutorialClicked,
+ backgroundColor = MaterialTheme.colorScheme.secondary,
+ modifier =
+ modifier.focusRequester(recentAppsFocusRequester).focusable().fillMaxSize(),
+ )
+ Spacer(modifier = Modifier.size(16.dp))
+ TutorialButton(
+ text = stringResource(R.string.touchpad_tutorial_switch_apps_gesture_button),
+ icon = ImageVector.vectorResource(id = R.drawable.touchpad_tutorial_apps_icon),
+ iconColor = MaterialTheme.colorScheme.primary,
+ onClick = onSwitchAppsTutorialClicked,
+ backgroundColor = MaterialTheme.colorScheme.onPrimary,
+ modifier =
+ modifier.focusRequester(switchAppsFocusRequester).focusable().fillMaxSize(),
+ )
+ }
+ }
+}
+
+@Composable
+private fun HorizontalCompactSelectionButtons(
+ onBackTutorialClicked: () -> Unit,
+ onHomeTutorialClicked: () -> Unit,
+ onRecentAppsTutorialClicked: () -> Unit,
+ onSwitchAppsTutorialClicked: () -> Unit,
+ lastSelectedScreen: Screen,
+ modifier: Modifier = Modifier,
) {
Row(
- horizontalArrangement = Arrangement.spacedBy(20.dp),
+ horizontalArrangement = Arrangement.spacedBy(16.dp),
verticalAlignment = Alignment.CenterVertically,
modifier = modifier,
) {
@@ -133,8 +233,8 @@ private fun HorizontalSelectionButtons(
onHomeTutorialClicked,
onRecentAppsTutorialClicked,
onSwitchAppsTutorialClicked,
- modifier = Modifier.weight(1f).fillMaxSize(),
lastSelectedScreen,
+ modifier = Modifier.weight(1f).fillMaxSize(),
)
}
}
@@ -145,8 +245,8 @@ private fun VerticalSelectionButtons(
onHomeTutorialClicked: () -> Unit,
onRecentAppsTutorialClicked: () -> Unit,
onSwitchAppsTutorialClicked: () -> Unit,
- modifier: Modifier = Modifier,
lastSelectedScreen: Screen,
+ modifier: Modifier = Modifier,
) {
Column(
verticalArrangement = Arrangement.spacedBy(16.dp),
@@ -158,8 +258,8 @@ private fun VerticalSelectionButtons(
onHomeTutorialClicked,
onRecentAppsTutorialClicked,
onSwitchAppsTutorialClicked,
- modifier = Modifier.weight(1f).fillMaxSize(),
lastSelectedScreen,
+ modifier = Modifier.weight(1f).fillMaxSize(),
)
}
}
@@ -170,8 +270,8 @@ private fun FourTutorialButtons(
onHomeTutorialClicked: () -> Unit,
onRecentAppsTutorialClicked: () -> Unit,
onSwitchAppsTutorialClicked: () -> Unit,
- modifier: Modifier = Modifier,
lastSelectedScreen: Screen,
+ modifier: Modifier = Modifier,
) {
val homeFocusRequester = remember { FocusRequester() }
val backFocusRequester = remember { FocusRequester() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorKosmos.kt
index 2bd104dd375d..48b801cb06be 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorKosmos.kt
@@ -20,6 +20,7 @@ import com.android.systemui.authentication.data.repository.authenticationReposit
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.log.table.logcatTableLogBuffer
import com.android.systemui.user.domain.interactor.selectedUserInteractor
val Kosmos.authenticationInteractor by
@@ -29,5 +30,6 @@ val Kosmos.authenticationInteractor by
backgroundDispatcher = testDispatcher,
repository = authenticationRepository,
selectedUserInteractor = selectedUserInteractor,
+ tableLogBuffer = logcatTableLogBuffer(this, "sceneFrameworkTableLogBuffer"),
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/common/data/repository/BatteryRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/common/data/repository/BatteryRepositoryKosmos.kt
new file mode 100644
index 000000000000..edfe8ecd0775
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/common/data/repository/BatteryRepositoryKosmos.kt
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.common.data.repository
+
+import com.android.systemui.kosmos.Kosmos
+
+val Kosmos.batteryRepository: BatteryRepository by Kosmos.Fixture { FakeBatteryRepository() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/common/data/repository/FakeBatteryRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/common/data/repository/FakeBatteryRepository.kt
new file mode 100644
index 000000000000..ac94335b42c3
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/common/data/repository/FakeBatteryRepository.kt
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.common.data.repository
+
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.asStateFlow
+
+class FakeBatteryRepository : BatteryRepository {
+ private val _isDevicePluggedIn = MutableStateFlow(false)
+
+ override val isDevicePluggedIn: Flow<Boolean> = _isDevicePluggedIn.asStateFlow()
+
+ fun setDevicePluggedIn(isPluggedIn: Boolean) {
+ _isDevicePluggedIn.value = isPluggedIn
+ }
+}
+
+val BatteryRepository.fake
+ get() = this as FakeBatteryRepository
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/common/domain/interactor/BatteryInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/common/domain/interactor/BatteryInteractorKosmos.kt
new file mode 100644
index 000000000000..2153955f3cc1
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/common/domain/interactor/BatteryInteractorKosmos.kt
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.common.domain.interactor
+
+import com.android.systemui.common.data.repository.batteryRepository
+import com.android.systemui.kosmos.Kosmos
+
+var Kosmos.batteryInteractor by Kosmos.Fixture { BatteryInteractor(batteryRepository) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalPrefsRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalPrefsRepository.kt
index 163625747d85..603160dea715 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalPrefsRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalPrefsRepository.kt
@@ -25,7 +25,8 @@ import kotlinx.coroutines.flow.map
/** Fake implementation of [CommunalPrefsRepository] */
class FakeCommunalPrefsRepository : CommunalPrefsRepository {
private val _isCtaDismissed = MutableStateFlow<Set<UserInfo>>(emptySet())
- private val _isHubOnboardingismissed = MutableStateFlow<Set<UserInfo>>(emptySet())
+ private val _isHubOnboardingDismissed = MutableStateFlow<Set<UserInfo>>(emptySet())
+ private val _isDreamButtonTooltipDismissed = MutableStateFlow<Set<UserInfo>>(emptySet())
override fun isCtaDismissed(user: UserInfo): Flow<Boolean> =
_isCtaDismissed.map { it.contains(user) }
@@ -35,10 +36,18 @@ class FakeCommunalPrefsRepository : CommunalPrefsRepository {
}
override fun isHubOnboardingDismissed(user: UserInfo): Flow<Boolean> =
- _isHubOnboardingismissed.map { it.contains(user) }
+ _isHubOnboardingDismissed.map { it.contains(user) }
override suspend fun setHubOnboardingDismissed(user: UserInfo) {
- _isHubOnboardingismissed.value =
- _isHubOnboardingismissed.value.toMutableSet().apply { add(user) }
+ _isHubOnboardingDismissed.value =
+ _isHubOnboardingDismissed.value.toMutableSet().apply { add(user) }
+ }
+
+ override fun isDreamButtonTooltipDismissed(user: UserInfo): Flow<Boolean> =
+ _isDreamButtonTooltipDismissed.map { it.contains(user) }
+
+ override suspend fun setDreamButtonTooltipDismissed(user: UserInfo) {
+ _isDreamButtonTooltipDismissed.value =
+ _isDreamButtonTooltipDismissed.value.toMutableSet().apply { add(user) }
}
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorKosmos.kt
index 89aad4be7cc0..b0a6de1f931a 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorKosmos.kt
@@ -19,10 +19,13 @@ package com.android.systemui.communal.domain.interactor
import android.content.testableContext
import android.os.userManager
import com.android.systemui.broadcast.broadcastDispatcher
+import com.android.systemui.common.domain.interactor.batteryInteractor
import com.android.systemui.communal.data.repository.communalMediaRepository
import com.android.systemui.communal.data.repository.communalSmartspaceRepository
import com.android.systemui.communal.data.repository.communalWidgetRepository
+import com.android.systemui.communal.posturing.domain.interactor.posturingInteractor
import com.android.systemui.communal.widgets.EditWidgetsActivityStarter
+import com.android.systemui.dock.dockManager
import com.android.systemui.flags.Flags
import com.android.systemui.flags.fakeFeatureFlagsClassic
import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
@@ -64,6 +67,9 @@ val Kosmos.communalInteractor by Fixture {
logBuffer = logcatLogBuffer("CommunalInteractor"),
tableLogBuffer = mock(),
managedProfileController = fakeManagedProfileController,
+ batteryInteractor = batteryInteractor,
+ dockManager = dockManager,
+ posturingInteractor = posturingInteractor,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/ui/viewmodel/CommunalToDreamButtonViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/ui/viewmodel/CommunalToDreamButtonViewModelKosmos.kt
index c2d2392186b7..43d3eb7b857a 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/ui/viewmodel/CommunalToDreamButtonViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/ui/viewmodel/CommunalToDreamButtonViewModelKosmos.kt
@@ -18,6 +18,7 @@ package com.android.systemui.communal.ui.viewmodel
import android.service.dream.dreamManager
import com.android.internal.logging.uiEventLogger
+import com.android.systemui.communal.domain.interactor.communalPrefsInteractor
import com.android.systemui.communal.domain.interactor.communalSettingsInteractor
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testDispatcher
@@ -29,6 +30,7 @@ val Kosmos.communalToDreamButtonViewModel by
CommunalToDreamButtonViewModel(
backgroundContext = testDispatcher,
batteryController = batteryController,
+ prefsInteractor = communalPrefsInteractor,
settingsInteractor = communalSettingsInteractor,
activityStarter = activityStarter,
dreamManager = dreamManager,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorKosmos.kt
index 1d3fd300da06..c927b5563bba 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorKosmos.kt
@@ -22,6 +22,7 @@ import com.android.systemui.deviceentry.data.repository.deviceEntryRepository
import com.android.systemui.keyguard.dismissCallbackRegistry
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.log.table.logcatTableLogBuffer
import com.android.systemui.scene.domain.interactor.sceneBackInteractor
import com.android.systemui.scene.domain.interactor.sceneInteractor
@@ -36,5 +37,6 @@ val Kosmos.deviceEntryInteractor by
alternateBouncerInteractor = alternateBouncerInteractor,
dismissCallbackRegistry = dismissCallbackRegistry,
sceneBackInteractor = sceneBackInteractor,
+ tableLogBuffer = logcatTableLogBuffer(this, "sceneFrameworkTableLogBuffer"),
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceUnlockedInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceUnlockedInteractorKosmos.kt
index e4c7df64fdc6..9e36428d119d 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceUnlockedInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceUnlockedInteractorKosmos.kt
@@ -25,6 +25,7 @@ import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
import com.android.systemui.kosmos.testScope
import com.android.systemui.lifecycle.activateIn
+import com.android.systemui.log.table.logcatTableLogBuffer
import com.android.systemui.power.domain.interactor.powerInteractor
import com.android.systemui.util.settings.data.repository.userAwareSecureSettingsRepository
@@ -40,6 +41,7 @@ val Kosmos.deviceUnlockedInteractor by Fixture {
systemPropertiesHelper = fakeSystemPropertiesHelper,
userAwareSecureSettingsRepository = userAwareSecureSettingsRepository,
keyguardInteractor = keyguardInteractor,
+ tableLogBuffer = logcatTableLogBuffer(this, "sceneFrameworkTableLogBuffer"),
)
.apply { activateIn(testScope) }
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorKosmos.kt
index b07de16be567..ff7a06c5087e 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorKosmos.kt
@@ -16,6 +16,8 @@
package com.android.systemui.keyguard.domain.interactor
+import com.android.systemui.communal.domain.interactor.communalInteractor
+import com.android.systemui.communal.domain.interactor.communalSceneInteractor
import com.android.systemui.communal.domain.interactor.communalSettingsInteractor
import com.android.systemui.keyguard.data.repository.keyguardTransitionRepository
import com.android.systemui.kosmos.Kosmos
@@ -41,5 +43,7 @@ var Kosmos.fromLockscreenTransitionInteractor by
communalSettingsInteractor = communalSettingsInteractor,
swipeToDismissInteractor = swipeToDismissInteractor,
keyguardOcclusionInteractor = keyguardOcclusionInteractor,
+ communalInteractor = communalInteractor,
+ communalSceneInteractor = communalSceneInteractor,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/interactor/SceneBackInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/interactor/SceneBackInteractorKosmos.kt
index e46ede65bfb6..e9ba42547883 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/interactor/SceneBackInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/interactor/SceneBackInteractorKosmos.kt
@@ -18,6 +18,7 @@ package com.android.systemui.scene.domain.interactor
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
+import com.android.systemui.log.table.logcatTableLogBuffer
import com.android.systemui.scene.sceneContainerConfig
import com.android.systemui.scene.shared.logger.sceneLogger
@@ -25,5 +26,6 @@ val Kosmos.sceneBackInteractor by Fixture {
SceneBackInteractor(
logger = sceneLogger,
sceneContainerConfig = sceneContainerConfig,
+ tableLogBuffer = logcatTableLogBuffer(this, "sceneFrameworkTableLogBuffer"),
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/startable/SceneContainerStartableKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/startable/SceneContainerStartableKosmos.kt
index d105326ec3d0..65bfafbfa9b0 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/startable/SceneContainerStartableKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/startable/SceneContainerStartableKosmos.kt
@@ -36,6 +36,7 @@ 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.kosmos.testScope
+import com.android.systemui.log.table.logcatTableLogBuffer
import com.android.systemui.model.sysUiState
import com.android.systemui.power.domain.interactor.powerInteractor
import com.android.systemui.scene.domain.interactor.disabledContentInteractor
@@ -89,5 +90,6 @@ val Kosmos.sceneContainerStartable by Fixture {
disabledContentInteractor = disabledContentInteractor,
activityTransitionAnimator = activityTransitionAnimator,
shadeModeInteractor = shadeModeInteractor,
+ tableLogBuffer = logcatTableLogBuffer(this, "sceneFrameworkTableLogBuffer"),
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeModeInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeModeInteractorKosmos.kt
index a4631f17cb37..2ba9c8094aac 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeModeInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeModeInteractorKosmos.kt
@@ -21,6 +21,7 @@ import android.provider.Settings
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.log.table.logcatTableLogBuffer
import com.android.systemui.res.R
import com.android.systemui.shade.data.repository.fakeShadeRepository
import com.android.systemui.shade.data.repository.shadeRepository
@@ -31,6 +32,7 @@ val Kosmos.shadeModeInteractor by Fixture {
applicationScope = applicationCoroutineScope,
repository = shadeRepository,
secureSettingsRepository = fakeSecureSettingsRepository,
+ tableLogBuffer = logcatTableLogBuffer(this, "sceneFrameworkTableLogBuffer"),
)
}
diff --git a/services/core/java/com/android/server/DockObserver.java b/services/core/java/com/android/server/DockObserver.java
index 3de84f17b583..d2db8f74cd05 100644
--- a/services/core/java/com/android/server/DockObserver.java
+++ b/services/core/java/com/android/server/DockObserver.java
@@ -27,7 +27,6 @@ import android.media.RingtoneManager;
import android.net.Uri;
import android.os.Binder;
import android.os.Handler;
-import android.os.Message;
import android.os.PowerManager;
import android.os.SystemClock;
import android.os.UEventObserver;
@@ -37,6 +36,7 @@ import android.util.Pair;
import android.util.Slog;
import com.android.internal.R;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.FrameworkStatsLog;
@@ -57,8 +57,6 @@ import java.util.Map;
final class DockObserver extends SystemService {
private static final String TAG = "DockObserver";
- private static final int MSG_DOCK_STATE_CHANGED = 0;
-
private final PowerManager mPowerManager;
private final PowerManager.WakeLock mWakeLock;
@@ -66,11 +64,16 @@ final class DockObserver extends SystemService {
private boolean mSystemReady;
+ @GuardedBy("mLock")
private int mActualDockState = Intent.EXTRA_DOCK_STATE_UNDOCKED;
+ @GuardedBy("mLock")
private int mReportedDockState = Intent.EXTRA_DOCK_STATE_UNDOCKED;
+
+ @GuardedBy("mLock")
private int mPreviousDockState = Intent.EXTRA_DOCK_STATE_UNDOCKED;
+ @GuardedBy("mLock")
private boolean mUpdatesStopped;
private final boolean mKeepDreamingWhenUnplugging;
@@ -182,18 +185,24 @@ final class DockObserver extends SystemService {
ExtconInfo.EXTCON_DOCK
});
- if (!infos.isEmpty()) {
- ExtconInfo info = infos.get(0);
- Slog.i(TAG, "Found extcon info devPath: " + info.getDevicePath()
- + ", statePath: " + info.getStatePath());
-
- // set initial status
- setDockStateFromProviderLocked(ExtconStateProvider.fromFile(info.getStatePath()));
- mPreviousDockState = mActualDockState;
-
- mExtconUEventObserver.startObserving(info);
- } else {
- Slog.i(TAG, "No extcon dock device found in this kernel.");
+ synchronized (mLock) {
+ if (!infos.isEmpty()) {
+ ExtconInfo info = infos.get(0);
+ Slog.i(
+ TAG,
+ "Found extcon info devPath: "
+ + info.getDevicePath()
+ + ", statePath: "
+ + info.getStatePath());
+
+ // set initial status
+ setDockStateFromProviderLocked(ExtconStateProvider.fromFile(info.getStatePath()));
+ mPreviousDockState = mActualDockState;
+
+ mExtconUEventObserver.startObserving(info);
+ } else {
+ Slog.i(TAG, "No extcon dock device found in this kernel.");
+ }
}
mDockObserverLocalService = new DockObserverLocalService();
@@ -223,13 +232,15 @@ final class DockObserver extends SystemService {
}
}
+ @GuardedBy("mLock")
private void updateIfDockedLocked() {
// don't bother broadcasting undocked here
if (mReportedDockState != Intent.EXTRA_DOCK_STATE_UNDOCKED) {
- updateLocked();
+ postWakefulDockStateChange();
}
}
+ @GuardedBy("mLock")
private void setActualDockStateLocked(int newState) {
mActualDockState = newState;
if (!mUpdatesStopped) {
@@ -237,6 +248,7 @@ final class DockObserver extends SystemService {
}
}
+ @GuardedBy("mLock")
private void setDockStateLocked(int newState) {
if (newState != mReportedDockState) {
mReportedDockState = newState;
@@ -246,10 +258,12 @@ final class DockObserver extends SystemService {
if (mSystemReady) {
// Wake up immediately when docked or undocked unless prohibited from doing so.
if (allowWakeFromDock()) {
- mPowerManager.wakeUp(SystemClock.uptimeMillis(),
+ mPowerManager.wakeUp(
+ SystemClock.uptimeMillis(),
+ PowerManager.WAKE_REASON_DOCK,
"android.server:DOCK");
}
- updateLocked();
+ postWakefulDockStateChange();
}
}
}
@@ -263,9 +277,8 @@ final class DockObserver extends SystemService {
Settings.Global.THEATER_MODE_ON, 0) == 0);
}
- private void updateLocked() {
- mWakeLock.acquire();
- mHandler.sendEmptyMessage(MSG_DOCK_STATE_CHANGED);
+ private void postWakefulDockStateChange() {
+ mHandler.post(mWakeLock.wrap(this::handleDockStateChange));
}
private void handleDockStateChange() {
@@ -348,17 +361,7 @@ final class DockObserver extends SystemService {
}
}
- private final Handler mHandler = new Handler(true /*async*/) {
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case MSG_DOCK_STATE_CHANGED:
- handleDockStateChange();
- mWakeLock.release();
- break;
- }
- }
- };
+ private final Handler mHandler = new Handler(true /*async*/);
private int getDockedStateExtraValue(ExtconStateProvider state) {
for (ExtconStateConfig config : mExtconStateConfigs) {
@@ -386,6 +389,7 @@ final class DockObserver extends SystemService {
}
}
+ @GuardedBy("mLock")
private void setDockStateFromProviderLocked(ExtconStateProvider provider) {
int state = Intent.EXTRA_DOCK_STATE_UNDOCKED;
if ("1".equals(provider.getValue("DOCK"))) {
diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java
index d335529a006a..ce526e510053 100644
--- a/services/core/java/com/android/server/am/CachedAppOptimizer.java
+++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java
@@ -572,6 +572,7 @@ public class CachedAppOptimizer {
public long mTotalAnonMemFreedKBs;
public long mSumOrigAnonRss;
public double mMaxCompactEfficiency;
+ public double mMaxSwapEfficiency;
// Cpu time
public long mTotalCpuTimeMillis;
@@ -586,6 +587,10 @@ public class CachedAppOptimizer {
if (compactEfficiency > mMaxCompactEfficiency) {
mMaxCompactEfficiency = compactEfficiency;
}
+ final double swapEfficiency = anonRssSaved / (double) origAnonRss;
+ if (swapEfficiency > mMaxSwapEfficiency) {
+ mMaxSwapEfficiency = swapEfficiency;
+ }
mTotalDeltaAnonRssKBs += anonRssSaved;
mTotalZramConsumedKBs += zramConsumed;
mTotalAnonMemFreedKBs += memFreed;
@@ -628,7 +633,11 @@ public class CachedAppOptimizer {
pw.println(" -----Memory Stats----");
pw.println(" Total Delta Anon RSS (KB) : " + mTotalDeltaAnonRssKBs);
pw.println(" Total Physical ZRAM Consumed (KB): " + mTotalZramConsumedKBs);
+ // Anon Mem Freed = Delta Anon RSS - ZRAM Consumed
pw.println(" Total Anon Memory Freed (KB): " + mTotalAnonMemFreedKBs);
+ pw.println(" Avg Swap Efficiency (KB) (Delta Anon RSS/Orig Anon RSS): "
+ + (mTotalDeltaAnonRssKBs / (double) mSumOrigAnonRss));
+ pw.println(" Max Swap Efficiency: " + mMaxSwapEfficiency);
// This tells us how much anon memory we were able to free thanks to running
// compaction
pw.println(" Avg Compaction Efficiency (Anon Freed/Anon RSS): "
@@ -808,8 +817,9 @@ public class CachedAppOptimizer {
pw.println(" Tracking last compaction stats for " + mLastCompactionStats.size()
+ " processes.");
pw.println("Last Compaction per process stats:");
- pw.println(" (ProcessName,Source,DeltaAnonRssKBs,ZramConsumedKBs,AnonMemFreedKBs,"
- + "CompactEfficiency,CompactCost(ms/MB),procState,oomAdj,oomAdjReason)");
+ pw.println(" (ProcessName,Source,DeltaAnonRssKBs,ZramConsumedKBs,AnonMemFreedKBs"
+ + ",SwapEfficiency,CompactEfficiency,CompactCost(ms/MB),procState,oomAdj,"
+ + "oomAdjReason)");
for (Map.Entry<Integer, SingleCompactionStats> entry :
mLastCompactionStats.entrySet()) {
SingleCompactionStats stats = entry.getValue();
@@ -818,7 +828,8 @@ public class CachedAppOptimizer {
pw.println();
pw.println("Last 20 Compactions Stats:");
pw.println(" (ProcessName,Source,DeltaAnonRssKBs,ZramConsumedKBs,AnonMemFreedKBs,"
- + "CompactEfficiency,CompactCost(ms/MB),procState,oomAdj,oomAdjReason)");
+ + "SwapEfficiency,CompactEfficiency,CompactCost(ms/MB),procState,oomAdj,"
+ + "oomAdjReason)");
for (SingleCompactionStats stats : mCompactionStatsHistory) {
stats.dump(pw);
}
@@ -1779,6 +1790,8 @@ public class CachedAppOptimizer {
double getCompactEfficiency() { return mAnonMemFreedKBs / (double) mOrigAnonRss; }
+ double getSwapEfficiency() { return mDeltaAnonRssKBs / (double) mOrigAnonRss; }
+
double getCompactCost() {
// mCpuTimeMillis / (anonMemFreedKBs/1024) and metric is in (ms/MB)
return mCpuTimeMillis / (double) mAnonMemFreedKBs * 1024;
@@ -1791,7 +1804,8 @@ public class CachedAppOptimizer {
@NeverCompile
void dump(PrintWriter pw) {
pw.println(" (" + mProcessName + "," + mSourceType.name() + "," + mDeltaAnonRssKBs
- + "," + mZramConsumedKBs + "," + mAnonMemFreedKBs + "," + getCompactEfficiency()
+ + "," + mZramConsumedKBs + "," + mAnonMemFreedKBs + ","
+ + getSwapEfficiency() + "," + getCompactEfficiency()
+ "," + getCompactCost() + "," + mProcState + "," + mOomAdj + ","
+ OomAdjuster.oomAdjReasonToString(mOomAdjReason) + ")");
}
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 5f118cb06d74..f2830090e7db 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -837,6 +837,7 @@ public class AudioService extends IAudioService.Stub
private final Executor mAudioServerLifecycleExecutor;
private long mSysPropListenerNativeHandle;
+ private CacheWatcher mCacheWatcher;
private final List<Future> mScheduledPermissionTasks = new ArrayList();
private IMediaProjectionManager mProjectionService; // to validate projection token
@@ -11093,31 +11094,26 @@ public class AudioService extends IAudioService.Stub
}, getAudioPermissionsDelay(), TimeUnit.MILLISECONDS));
}
};
- mSysPropListenerNativeHandle = mAudioSystem.listenForSystemPropertyChange(
- PermissionManager.CACHE_KEY_PACKAGE_INFO_NOTIFY,
- task);
+ if (PropertyInvalidatedCache.separatePermissionNotificationsEnabled()) {
+ mCacheWatcher = new CacheWatcher(task);
+ mCacheWatcher.start();
+ } else {
+ mSysPropListenerNativeHandle = mAudioSystem.listenForSystemPropertyChange(
+ PermissionManager.CACHE_KEY_PACKAGE_INFO_NOTIFY,
+ task);
+ }
} else {
mAudioSystem.listenForSystemPropertyChange(
PermissionManager.CACHE_KEY_PACKAGE_INFO_NOTIFY,
() -> mAudioServerLifecycleExecutor.execute(
mPermissionProvider::onPermissionStateChanged));
}
-
- if (PropertyInvalidatedCache.separatePermissionNotificationsEnabled()) {
- new PackageInfoTransducer().start();
- }
}
/**
- * A transducer that converts high-speed changes in the CACHE_KEY_PACKAGE_INFO_CACHE
- * PropertyInvalidatedCache into low-speed changes in the CACHE_KEY_PACKAGE_INFO_NOTIFY system
- * property. This operates on the popcorn principle: changes in the source are done when the
- * source has been quiet for the soak interval.
- *
- * TODO(b/381097912) This is a temporary measure to support migration away from sysprop
- * sniffing. It should be cleaned up.
+ * Listens for CACHE_KEY_PACKAGE_INFO_CACHE invalidations to trigger permission syncing
*/
- private static class PackageInfoTransducer extends Thread {
+ private static class CacheWatcher extends Thread {
// The run/stop signal.
private final AtomicBoolean mRunning = new AtomicBoolean(false);
@@ -11125,81 +11121,33 @@ public class AudioService extends IAudioService.Stub
// The source of change information.
private final PropertyInvalidatedCache.NonceWatcher mWatcher;
- // The handler for scheduling delayed reactions to changes.
- private final Handler mHandler;
+ // Task to trigger when cache changes
+ private final Runnable mTask;
- // How long to soak changes: 50ms is the legacy choice.
- private final static long SOAK_TIME_MS = 50;
-
- // The ubiquitous lock.
- private final Object mLock = new Object();
-
- // If positive, this is the soak expiration time.
- @GuardedBy("mLock")
- private long mSoakDeadlineMs = -1;
-
- // A source of unique long values.
- @GuardedBy("mLock")
- private long mToken = 0;
-
- PackageInfoTransducer() {
- mWatcher = PropertyInvalidatedCache
- .getNonceWatcher(PermissionManager.CACHE_KEY_PACKAGE_INFO_CACHE);
- mHandler = new Handler(BackgroundThread.getHandler().getLooper()) {
- @Override
- public void handleMessage(Message msg) {
- PackageInfoTransducer.this.handleMessage(msg);
- }};
+ public CacheWatcher(Runnable r) {
+ mWatcher = PropertyInvalidatedCache.getNonceWatcher(
+ PermissionManager.CACHE_KEY_PACKAGE_INFO_CACHE);
+ mTask = r;
}
public void run() {
mRunning.set(true);
while (mRunning.get()) {
+ doCheck();
try {
- final int changes = mWatcher.waitForChange();
- if (changes == 0 || !mRunning.get()) {
- continue;
- }
+ mWatcher.waitForChange();
} catch (InterruptedException e) {
+ Log.wtf(TAG, "Unexpected Interrupt", e);
// We don't know why the exception occurred but keep running until told to
// stop.
continue;
}
- trigger();
}
}
- @GuardedBy("mLock")
- private void updateLocked() {
- String n = Long.toString(mToken++);
- SystemPropertySetter.setWithRetry(PermissionManager.CACHE_KEY_PACKAGE_INFO_NOTIFY, n);
- }
-
- private void trigger() {
- synchronized (mLock) {
- boolean alreadyQueued = mSoakDeadlineMs >= 0;
- final long nowMs = SystemClock.uptimeMillis();
- mSoakDeadlineMs = nowMs + SOAK_TIME_MS;
- if (!alreadyQueued) {
- mHandler.sendEmptyMessageAtTime(0, mSoakDeadlineMs);
- updateLocked();
- }
- }
- }
-
- private void handleMessage(Message msg) {
- synchronized (mLock) {
- if (mSoakDeadlineMs < 0) {
- return; // ???
- }
- final long nowMs = SystemClock.uptimeMillis();
- if (mSoakDeadlineMs > nowMs) {
- mSoakDeadlineMs = nowMs + SOAK_TIME_MS;
- mHandler.sendEmptyMessageAtTime(0, mSoakDeadlineMs);
- return;
- }
- mSoakDeadlineMs = -1;
- updateLocked();
+ public synchronized void doCheck() {
+ if (mWatcher.isChanged()) {
+ mTask.run();
}
}
@@ -15376,7 +15324,11 @@ public class AudioService extends IAudioService.Stub
/** @see AudioManager#permissionUpdateBarrier() */
public void permissionUpdateBarrier() {
if (!audioserverPermissions()) return;
- mAudioSystem.triggerSystemPropertyUpdate(mSysPropListenerNativeHandle);
+ if (PropertyInvalidatedCache.separatePermissionNotificationsEnabled()) {
+ mCacheWatcher.doCheck();
+ } else {
+ mAudioSystem.triggerSystemPropertyUpdate(mSysPropListenerNativeHandle);
+ }
List<Future> snapshot;
synchronized (mScheduledPermissionTasks) {
snapshot = List.copyOf(mScheduledPermissionTasks);
diff --git a/services/core/java/com/android/server/wm/AppCompatAspectRatioPolicy.java b/services/core/java/com/android/server/wm/AppCompatAspectRatioPolicy.java
index ab1778a1a32e..15c0789d777e 100644
--- a/services/core/java/com/android/server/wm/AppCompatAspectRatioPolicy.java
+++ b/services/core/java/com/android/server/wm/AppCompatAspectRatioPolicy.java
@@ -295,9 +295,14 @@ class AppCompatAspectRatioPolicy {
// {@link ActivityRecord#shouldCreateAppCompatDisplayInsets()} will be false for
// both activities that are naturally resizeable and activities that have been
// forced resizeable.
+ // Camera compat mode is an exception to this, where the activity is letterboxed
+ // to an aspect ratio commonly found on phones, e.g. 16:9, to avoid issues like
+ // stretching of the camera preview.
|| (Flags.ignoreAspectRatioRestrictionsForResizeableFreeformActivities()
&& task.getWindowingMode() == WINDOWING_MODE_FREEFORM
- && !mActivityRecord.shouldCreateAppCompatDisplayInsets())) {
+ && !mActivityRecord.shouldCreateAppCompatDisplayInsets()
+ && !AppCompatCameraPolicy.shouldCameraCompatControlAspectRatio(
+ mActivityRecord))) {
return false;
}
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index e1f3f0ef5615..bf4595c815bd 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -94,8 +94,6 @@ namespace input_flags = com::android::input::flags;
namespace android {
-static const bool ENABLE_INPUT_FILTER_RUST = input_flags::enable_input_filter_rust_impl();
-
// The exponent used to calculate the pointer speed scaling factor.
// The scaling factor is calculated as 2 ^ (speed * exponent),
// where the speed ranges from -7 to + 7 and is supplied by the user.
@@ -3248,27 +3246,21 @@ static void nativeSetStylusPointerIconEnabled(JNIEnv* env, jobject nativeImplObj
static void nativeSetAccessibilityBounceKeysThreshold(JNIEnv* env, jobject nativeImplObj,
jint thresholdTimeMs) {
NativeInputManager* im = getNativeInputManager(env, nativeImplObj);
- if (ENABLE_INPUT_FILTER_RUST) {
- im->getInputManager()->getInputFilter().setAccessibilityBounceKeysThreshold(
- static_cast<nsecs_t>(thresholdTimeMs) * 1000000);
- }
+ im->getInputManager()->getInputFilter().setAccessibilityBounceKeysThreshold(
+ static_cast<nsecs_t>(thresholdTimeMs) * 1000000);
}
static void nativeSetAccessibilitySlowKeysThreshold(JNIEnv* env, jobject nativeImplObj,
jint thresholdTimeMs) {
NativeInputManager* im = getNativeInputManager(env, nativeImplObj);
- if (ENABLE_INPUT_FILTER_RUST) {
- im->getInputManager()->getInputFilter().setAccessibilitySlowKeysThreshold(
- static_cast<nsecs_t>(thresholdTimeMs) * 1000000);
- }
+ im->getInputManager()->getInputFilter().setAccessibilitySlowKeysThreshold(
+ static_cast<nsecs_t>(thresholdTimeMs) * 1000000);
}
static void nativeSetAccessibilityStickyKeysEnabled(JNIEnv* env, jobject nativeImplObj,
jboolean enabled) {
NativeInputManager* im = getNativeInputManager(env, nativeImplObj);
- if (ENABLE_INPUT_FILTER_RUST) {
- im->getInputManager()->getInputFilter().setAccessibilityStickyKeysEnabled(enabled);
- }
+ im->getInputManager()->getInputFilter().setAccessibilityStickyKeysEnabled(enabled);
}
static void nativeSetInputMethodConnectionIsActive(JNIEnv* env, jobject nativeImplObj,
diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
index 7af4ede05363..c3aa2894997d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -4953,7 +4953,8 @@ public class SizeCompatTests extends WindowTestsBase {
}
@Test
- @EnableFlags(Flags.FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
+ @EnableFlags({Flags.FLAG_IGNORE_ASPECT_RATIO_RESTRICTIONS_FOR_RESIZEABLE_FREEFORM_ACTIVITIES,
+ Flags.FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING})
public void testCameraCompatAspectRatioAppliedForFixedOrientationCameraActivities() {
// Needed to create camera compat policy in DisplayContent.
allowDesktopMode();
@@ -4965,7 +4966,8 @@ public class SizeCompatTests extends WindowTestsBase {
setupCameraCompatAspectRatio(cameraCompatAspectRatio, display);
// Create task on test display.
- final Task task = new TaskBuilder(mSupervisor).setDisplay(display).build();
+ final Task task = new TaskBuilder(mSupervisor).setDisplay(display)
+ .setWindowingMode(WINDOWING_MODE_FREEFORM).build();
// Create fixed portrait activity.
final ActivityRecord fixedOrientationActivity = new ActivityBuilder(mAtm)
@@ -4978,7 +4980,8 @@ public class SizeCompatTests extends WindowTestsBase {
}
@Test
- @EnableFlags(Flags.FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
+ @EnableFlags({Flags.FLAG_IGNORE_ASPECT_RATIO_RESTRICTIONS_FOR_RESIZEABLE_FREEFORM_ACTIVITIES,
+ Flags.FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING})
public void testCameraCompatAspectRatioForFixedOrientationCameraActivitiesPortraitWindow() {
// Needed to create camera compat policy in DisplayContent.
allowDesktopMode();
@@ -4990,7 +4993,8 @@ public class SizeCompatTests extends WindowTestsBase {
setupCameraCompatAspectRatio(cameraCompatAspectRatio, display);
// Create task on test display.
- final Task task = new TaskBuilder(mSupervisor).setDisplay(display).build();
+ final Task task = new TaskBuilder(mSupervisor).setDisplay(display)
+ .setWindowingMode(WINDOWING_MODE_FREEFORM).build();
// Create fixed portrait activity.
final ActivityRecord fixedOrientationActivity = new ActivityBuilder(mAtm)
@@ -5003,7 +5007,8 @@ public class SizeCompatTests extends WindowTestsBase {
}
@Test
- @EnableFlags(Flags.FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
+ @EnableFlags({Flags.FLAG_IGNORE_ASPECT_RATIO_RESTRICTIONS_FOR_RESIZEABLE_FREEFORM_ACTIVITIES,
+ Flags.FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING})
public void testCameraCompatAspectRatioAppliedInsteadOfDefaultAspectRatio() {
// Needed to create camera compat policy in DisplayContent.
allowDesktopMode();
@@ -5015,7 +5020,8 @@ public class SizeCompatTests extends WindowTestsBase {
setupCameraCompatAspectRatio(cameraCompatAspectRatio, display);
// Create task on test display.
- final Task task = new TaskBuilder(mSupervisor).setDisplay(display).build();
+ final Task task = new TaskBuilder(mSupervisor).setDisplay(display)
+ .setWindowingMode(WINDOWING_MODE_FREEFORM).build();
// App's target min aspect ratio - this should not be used, as camera controls aspect ratio.
final float targetMinAspectRatio = 4.0f;
@@ -5032,7 +5038,8 @@ public class SizeCompatTests extends WindowTestsBase {
}
@Test
- @EnableFlags(Flags.FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
+ @EnableFlags({Flags.FLAG_IGNORE_ASPECT_RATIO_RESTRICTIONS_FOR_RESIZEABLE_FREEFORM_ACTIVITIES,
+ Flags.FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING})
public void testCameraCompatAspectRatio_defaultAspectRatioAppliedWhenGreater() {
// Needed to create camera compat policy in DisplayContent.
allowDesktopMode();
@@ -5044,7 +5051,8 @@ public class SizeCompatTests extends WindowTestsBase {
setupCameraCompatAspectRatio(cameraCompatAspectRatio, display);
// Create task on test display.
- final Task task = new TaskBuilder(mSupervisor).setDisplay(display).build();
+ final Task task = new TaskBuilder(mSupervisor).setDisplay(display)
+ .setWindowingMode(WINDOWING_MODE_FREEFORM).build();
// App's target min aspect ratio bigger than camera compat aspect ratio - use that instead.
final float targetMinAspectRatio = 6.0f;
diff --git a/tests/CompanionDeviceMultiDeviceTests/host/Android.bp b/tests/CompanionDeviceMultiDeviceTests/host/Android.bp
index a0e047759dab..1fb18a6bb391 100644
--- a/tests/CompanionDeviceMultiDeviceTests/host/Android.bp
+++ b/tests/CompanionDeviceMultiDeviceTests/host/Android.bp
@@ -39,13 +39,4 @@ python_test_host {
device_common_data: [
":cdm_snippet_legacy",
],
- version: {
- py2: {
- enabled: false,
- },
- py3: {
- enabled: true,
- embedded_launcher: true,
- },
- },
}
diff --git a/tests/Input/src/android/hardware/input/StickyModifierStateListenerTest.kt b/tests/Input/src/android/hardware/input/StickyModifierStateListenerTest.kt
index 1c2a0538e552..c2f9adf84ccd 100644
--- a/tests/Input/src/android/hardware/input/StickyModifierStateListenerTest.kt
+++ b/tests/Input/src/android/hardware/input/StickyModifierStateListenerTest.kt
@@ -50,10 +50,7 @@ import org.mockito.junit.MockitoJUnitRunner
*/
@Presubmit
@RunWith(MockitoJUnitRunner::class)
-@EnableFlags(
- com.android.hardware.input.Flags.FLAG_KEYBOARD_A11Y_STICKY_KEYS_FLAG,
- com.android.input.flags.Flags.FLAG_ENABLE_INPUT_FILTER_RUST_IMPL,
-)
+@EnableFlags(com.android.hardware.input.Flags.FLAG_KEYBOARD_A11Y_STICKY_KEYS_FLAG)
class StickyModifierStateListenerTest {
@get:Rule