Merge "Pass statsToken from IMM to InsetsController when calling show/hide" into main
diff --git a/apct-tests/perftests/protolog/Android.bp b/apct-tests/perftests/protolog/Android.bp
new file mode 100644
index 0000000..08e365b
--- /dev/null
+++ b/apct-tests/perftests/protolog/Android.bp
@@ -0,0 +1,33 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+android_test {
+ name: "ProtologPerfTests",
+ team: "trendy_team_windowing_tools",
+ srcs: ["src/**/*.java"],
+ static_libs: [
+ "androidx.test.rules",
+ "androidx.annotation_annotation",
+ "apct-perftests-utils",
+ "collector-device-lib",
+ "platform-test-annotations",
+ ],
+ test_suites: [
+ "device-tests",
+ "automotive-tests",
+ ],
+ data: [":perfetto_artifacts"],
+ platform_apis: true,
+ certificate: "platform",
+}
diff --git a/apct-tests/perftests/protolog/AndroidManifest.xml b/apct-tests/perftests/protolog/AndroidManifest.xml
new file mode 100644
index 0000000..68125df
--- /dev/null
+++ b/apct-tests/perftests/protolog/AndroidManifest.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.perftests.protolog">
+
+ <!-- For perfetto trace files -->
+ <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+
+ <application>
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.perftests.protolog">
+ <!-- <meta-data android:name="listener" android:value="android.protolog.ProtologPerfRunListener" /> -->
+ </instrumentation>
+</manifest>
diff --git a/apct-tests/perftests/protolog/AndroidTest.xml b/apct-tests/perftests/protolog/AndroidTest.xml
new file mode 100644
index 0000000..871a20c
--- /dev/null
+++ b/apct-tests/perftests/protolog/AndroidTest.xml
@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2024 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<configuration description="Runs ProtologPerfTests metric instrumentation.">
+ <option name="test-suite-tag" value="apct" />
+ <option name="test-suite-tag" value="apct-metric-instrumentation" />
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="test-file-name" value="ProtologPerfTests.apk" />
+ </target_preparer>
+
+ <target_preparer class="com.android.tradefed.targetprep.DeviceSetup">
+ <option name="force-skip-system-props" value="true" />
+ <option name="run-command" value="input keyevent KEYCODE_WAKEUP" />
+ <option name="run-command" value="cmd window dismiss-keyguard" />
+ <option name="run-command" value="cmd package compile -m speed com.android.perftests.wm" />
+ </target_preparer>
+
+ <!-- Needed for pushing the trace config file -->
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="push-file" key="trace_config_detailed.textproto" value="/data/misc/perfetto-traces/trace_config.textproto" />
+ </target_preparer>
+
+ <!-- Needed for storing the perfetto trace files in the sdcard/test_results-->
+ <option name="isolated-storage" value="false" />
+
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="package" value="com.android.perftests.protolog" />
+ <option name="hidden-api-checks" value="false"/>
+
+ <!-- Listener related args for collecting the traces and waiting for the device to stabilize. -->
+ <option name="device-listeners" value="android.device.collectors.ProcLoadListener,android.device.collectors.PerfettoListener" />
+
+ <!-- Guarantee that user defined RunListeners will be running before any of the default listeners defined in this runner. -->
+ <option name="instrumentation-arg" key="newRunListenerMode" value="true" />
+
+ <!-- ProcLoadListener related arguments -->
+ <!-- Wait for device last minute threshold to reach 3 with 2 minute timeout before starting the test run -->
+ <option name="instrumentation-arg" key="procload-collector:per_run" value="true" />
+ <option name="instrumentation-arg" key="proc-loadavg-threshold" value="3" />
+ <option name="instrumentation-arg" key="proc-loadavg-timeout" value="120000" />
+ <option name="instrumentation-arg" key="proc-loadavg-interval" value="10000" />
+
+ <!-- PerfettoListener related arguments -->
+ <option name="instrumentation-arg" key="perfetto_config_text_proto" value="true" />
+ <option name="instrumentation-arg" key="perfetto_config_file" value="trace_config.textproto" />
+ </test>
+
+ <!-- <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
+ <option name="directory-keys" value="/data/local/tmp/ProtologPerfTests" /> -->
+ <!-- Needed for pulling the collected trace config on to the host -->
+ <!-- <option name="pull-pattern-keys" value="perfetto_file_path" />
+ </metrics_collector> -->
+</configuration>
diff --git a/apct-tests/perftests/protolog/OWNERS b/apct-tests/perftests/protolog/OWNERS
new file mode 100644
index 0000000..3f3308c
--- /dev/null
+++ b/apct-tests/perftests/protolog/OWNERS
@@ -0,0 +1 @@
+include platform/development:/tools/winscope/OWNERS
diff --git a/apct-tests/perftests/protolog/src/com/android/internal/protolog/ProtologPerfTest.java b/apct-tests/perftests/protolog/src/com/android/internal/protolog/ProtologPerfTest.java
new file mode 100644
index 0000000..e1edb37
--- /dev/null
+++ b/apct-tests/perftests/protolog/src/com/android/internal/protolog/ProtologPerfTest.java
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.protolog;
+
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+
+import com.android.internal.protolog.common.IProtoLogGroup;
+import com.android.internal.protolog.common.LogLevel;
+
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+import java.util.Arrays;
+import java.util.Collection;
+
+@RunWith(Parameterized.class)
+public class ProtologPerfTest {
+ @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+ @Parameters(name="logToProto_{0}_logToLogcat_{1}")
+ public static Collection<Object[]> params() {
+ return Arrays.asList(new Object[][] {
+ { true, true },
+ { true, false },
+ { false, true },
+ { false, false }
+ });
+ }
+
+ private final boolean mLogToProto;
+ private final boolean mLogToLogcat;
+
+ public ProtologPerfTest(boolean logToProto, boolean logToLogcat) {
+ mLogToProto = logToProto;
+ mLogToLogcat = logToLogcat;
+ }
+
+ @BeforeClass
+ public static void init() {
+ ProtoLog.init(TestProtoLogGroup.values());
+ }
+
+ @Before
+ public void setUp() {
+ TestProtoLogGroup.TEST_GROUP.setLogToProto(mLogToProto);
+ TestProtoLogGroup.TEST_GROUP.setLogToLogcat(mLogToLogcat);
+ }
+
+ @Test
+ public void logProcessedProtoLogMessageWithoutArgs() {
+ final var protoLog = ProtoLog.getSingleInstance();
+
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ protoLog.log(
+ LogLevel.INFO, TestProtoLogGroup.TEST_GROUP, 123,
+ 0, (Object[]) null);
+ }
+ }
+
+ @Test
+ public void logProcessedProtoLogMessageWithArgs() {
+ final var protoLog = ProtoLog.getSingleInstance();
+
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ protoLog.log(
+ LogLevel.INFO, TestProtoLogGroup.TEST_GROUP, 123,
+ 0b1110101001010100,
+ new Object[]{"test", 1, 2, 3, 0.4, 0.5, 0.6, true});
+ }
+ }
+
+ @Test
+ public void logNonProcessedProtoLogMessageWithNoArgs() {
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ ProtoLog.d(TestProtoLogGroup.TEST_GROUP, "Test message");
+ }
+ }
+
+ @Test
+ public void logNonProcessedProtoLogMessageWithArgs() {
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ ProtoLog.d(TestProtoLogGroup.TEST_GROUP, "Test messag %s, %d, %b", "arg1", 2, true);
+ }
+ }
+
+ private enum TestProtoLogGroup implements IProtoLogGroup {
+ TEST_GROUP(true, true, false, "WindowManagetProtoLogTest");
+
+ private final boolean mEnabled;
+ private volatile boolean mLogToProto;
+ private volatile boolean mLogToLogcat;
+ private final String mTag;
+
+ /**
+ * @param enabled set to false to exclude all log statements for this group from
+ * compilation, they will not be available in runtime.
+ * @param logToProto enable binary logging for the group
+ * @param logToLogcat enable text logging for the group
+ * @param tag name of the source of the logged message
+ */
+ TestProtoLogGroup(boolean enabled, boolean logToProto, boolean logToLogcat, String tag) {
+ this.mEnabled = enabled;
+ this.mLogToProto = logToProto;
+ this.mLogToLogcat = logToLogcat;
+ this.mTag = tag;
+ }
+
+ @Override
+ public boolean isEnabled() {
+ return mEnabled;
+ }
+
+ @Override
+ public boolean isLogToProto() {
+ return mLogToProto;
+ }
+
+ @Override
+ public boolean isLogToLogcat() {
+ return mLogToLogcat;
+ }
+
+ @Override
+ public boolean isLogToAny() {
+ return mLogToLogcat || mLogToProto;
+ }
+
+ @Override
+ public String getTag() {
+ return mTag;
+ }
+
+ @Override
+ public void setLogToProto(boolean logToProto) {
+ this.mLogToProto = logToProto;
+ }
+
+ @Override
+ public void setLogToLogcat(boolean logToLogcat) {
+ this.mLogToLogcat = logToLogcat;
+ }
+
+ @Override
+ public int getId() {
+ return ordinal();
+ }
+ }
+}
diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java
index 57acc71..918e591 100644
--- a/core/java/android/service/notification/ZenModeConfig.java
+++ b/core/java/android/service/notification/ZenModeConfig.java
@@ -241,10 +241,11 @@
// ZenModeConfig XML versions distinguishing key changes.
public static final int XML_VERSION_ZEN_UPGRADE = 8;
public static final int XML_VERSION_MODES_API = 11;
+ public static final int XML_VERSION_MODES_UI = 12;
- // TODO: b/310620812 - Update XML_VERSION and update default_zen_config.xml accordingly when
- // modes_api is inlined.
- private static final int XML_VERSION = 10;
+ // TODO: b/310620812, b/344831624 - Update XML_VERSION and update default_zen_config.xml
+ // accordingly when modes_api / modes_ui are inlined.
+ private static final int XML_VERSION_PRE_MODES = 10;
public static final String ZEN_TAG = "zen";
private static final String ZEN_ATT_VERSION = "version";
private static final String ZEN_ATT_USER = "user";
@@ -952,7 +953,13 @@
}
public static int getCurrentXmlVersion() {
- return Flags.modesApi() ? XML_VERSION_MODES_API : XML_VERSION;
+ if (Flags.modesUi()) {
+ return XML_VERSION_MODES_UI;
+ } else if (Flags.modesApi()) {
+ return XML_VERSION_MODES_API;
+ } else {
+ return XML_VERSION_PRE_MODES;
+ }
}
public static ZenModeConfig readXml(TypedXmlPullParser parser)
@@ -2607,7 +2614,7 @@
@AutomaticZenRule.Type
public int type = AutomaticZenRule.TYPE_UNKNOWN;
public String triggerDescription;
- public String iconResName;
+ @Nullable public String iconResName;
public boolean allowManualInvocation;
@AutomaticZenRule.ModifiableField public int userModifiedFields;
@ZenPolicy.ModifiableField public int zenPolicyUserModifiedFields;
diff --git a/core/java/android/webkit/WebSettings.java b/core/java/android/webkit/WebSettings.java
index fe26510..7366b9a 100644
--- a/core/java/android/webkit/WebSettings.java
+++ b/core/java/android/webkit/WebSettings.java
@@ -497,25 +497,32 @@
public abstract boolean getUseWebViewBackgroundForOverscrollBackground();
/**
- * Sets whether the WebView should save form data. In Android O, the
- * platform has implemented a fully functional Autofill feature to store
- * form data. Therefore, the Webview form data save feature is disabled.
+ * Sets whether the WebView should save form data. In {@link android.os.Build.VERSION_CODES#O},
+ * the platform has implemented a fully functional Autofill feature to store form data.
+ * Therefore, the Webview form data save feature is disabled.
*
- * Note that the feature will continue to be supported on older versions of
+ * <p>Note that the feature will continue to be supported on older versions of
* Android as before.
*
- * @deprecated In Android O and afterwards, this function does not have
- * any effect, the form data will be saved to platform's autofill service
- * if applicable.
+ * @see #getSaveFormData
+ * @deprecated In Android O and afterwards, this function does not have any effect. Form data
+ * will be saved to platform's autofill service if applicable.
*/
@Deprecated
public abstract void setSaveFormData(boolean save);
/**
- * Gets whether the WebView saves form data.
+ * Gets whether the WebView saves form data. In {@link android.os.Build.VERSION_CODES#O}, the
+ * platform has implemented a fully functional Autofill feature to store form data. Therefore,
+ * the Webview form data save feature is disabled.
+ *
+ * <p>Note that the feature will continue to be supported on older versions of
+ * Android as before.
*
* @return whether the WebView saves form data
* @see #setSaveFormData
+ * @deprecated In Android O and afterwards, this function does not have any effect. Form data
+ * will be filled from the platform's autofill service if applicable.
*/
@Deprecated
public abstract boolean getSaveFormData();
diff --git a/core/java/com/android/internal/protolog/LogcatOnlyProtoLogImpl.java b/core/java/com/android/internal/protolog/LogcatOnlyProtoLogImpl.java
index b82c660..34e0418 100644
--- a/core/java/com/android/internal/protolog/LogcatOnlyProtoLogImpl.java
+++ b/core/java/com/android/internal/protolog/LogcatOnlyProtoLogImpl.java
@@ -40,6 +40,8 @@
*/
@Deprecated
public class LogcatOnlyProtoLogImpl implements IProtoLog {
+ private static final String LOG_TAG = LogcatOnlyProtoLogImpl.class.getName();
+
@Override
public void log(LogLevel logLevel, IProtoLogGroup group, long messageHash, int paramsMask,
Object[] args) {
@@ -48,19 +50,21 @@
@Override
public void log(LogLevel logLevel, IProtoLogGroup group, String messageString, Object[] args) {
- if (REQUIRE_PROTOLOGTOOL) {
- throw new RuntimeException(
- "REQUIRE_PROTOLOGTOOL not set to false before the first log call.");
+ if (REQUIRE_PROTOLOGTOOL && group.isLogToProto()) {
+ Log.w(LOG_TAG, "ProtoLog message not processed. Failed to log it to proto. "
+ + "Logging it below to logcat instead.");
}
- String formattedString = TextUtils.formatSimple(messageString, args);
- switch (logLevel) {
- case VERBOSE -> Log.v(group.getTag(), formattedString);
- case INFO -> Log.i(group.getTag(), formattedString);
- case DEBUG -> Log.d(group.getTag(), formattedString);
- case WARN -> Log.w(group.getTag(), formattedString);
- case ERROR -> Log.e(group.getTag(), formattedString);
- case WTF -> Log.wtf(group.getTag(), formattedString);
+ if (group.isLogToLogcat() || group.isLogToProto()) {
+ String formattedString = TextUtils.formatSimple(messageString, args);
+ switch (logLevel) {
+ case VERBOSE -> Log.v(group.getTag(), formattedString);
+ case INFO -> Log.i(group.getTag(), formattedString);
+ case DEBUG -> Log.d(group.getTag(), formattedString);
+ case WARN -> Log.w(group.getTag(), formattedString);
+ case ERROR -> Log.e(group.getTag(), formattedString);
+ case WTF -> Log.wtf(group.getTag(), formattedString);
+ }
}
}
diff --git a/core/java/com/android/internal/protolog/ProtoLog.java b/core/java/com/android/internal/protolog/ProtoLog.java
index 660d3c9..bf77db7 100644
--- a/core/java/com/android/internal/protolog/ProtoLog.java
+++ b/core/java/com/android/internal/protolog/ProtoLog.java
@@ -63,6 +63,9 @@
* @param groups The ProtoLog groups that will be used in the process.
*/
public static void init(IProtoLogGroup... groups) {
+ // These tracing instances are only used when we cannot or do not preprocess the source
+ // files to extract out the log strings. Otherwise, the trace calls are replaced with calls
+ // directly to the generated tracing implementations.
if (android.tracing.Flags.perfettoProtologTracing()) {
synchronized (sInitLock) {
if (sProtoLogInstance != null) {
@@ -76,8 +79,6 @@
sProtoLogInstance = new PerfettoProtoLogImpl(groups);
}
} else {
- // The first call to ProtoLog is likely to flip REQUIRE_PROTOLOGTOOL, which is when this
- // static block will be executed before REQUIRE_PROTOLOGTOOL is actually set.
sProtoLogInstance = new LogcatOnlyProtoLogImpl();
}
}
diff --git a/core/jni/android_graphics_SurfaceTexture.cpp b/core/jni/android_graphics_SurfaceTexture.cpp
index 50832a5..8dd63cc 100644
--- a/core/jni/android_graphics_SurfaceTexture.cpp
+++ b/core/jni/android_graphics_SurfaceTexture.cpp
@@ -256,9 +256,21 @@
}
}
-static void SurfaceTexture_init(JNIEnv* env, jobject thiz, jboolean isDetached,
- jint texName, jboolean singleBufferMode, jobject weakThiz)
-{
+static void SurfaceTexture_init(JNIEnv* env, jobject thiz, jboolean isDetached, jint texName,
+ jboolean singleBufferMode, jobject weakThiz) {
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+ sp<SurfaceTexture> surfaceTexture;
+ if (isDetached) {
+ surfaceTexture = new SurfaceTexture(GL_TEXTURE_EXTERNAL_OES, true, !singleBufferMode);
+ } else {
+ surfaceTexture =
+ new SurfaceTexture(texName, GL_TEXTURE_EXTERNAL_OES, true, !singleBufferMode);
+ }
+
+ if (singleBufferMode) {
+ surfaceTexture->setMaxBufferCount(1);
+ }
+#else
sp<IGraphicBufferProducer> producer;
sp<IGraphicBufferConsumer> consumer;
BufferQueue::createBufferQueue(&producer, &consumer);
@@ -275,6 +287,7 @@
surfaceTexture = new SurfaceTexture(consumer, texName,
GL_TEXTURE_EXTERNAL_OES, true, !singleBufferMode);
}
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
if (surfaceTexture == 0) {
jniThrowException(env, OutOfResourcesException,
@@ -287,11 +300,27 @@
createProcessUniqueId()));
// If the current context is protected, inform the producer.
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+ surfaceTexture->setConsumerIsProtected(isProtectedContext());
+
+ SurfaceTexture_setSurfaceTexture(env, thiz, surfaceTexture);
+ sp<Surface> surface = surfaceTexture->getSurface();
+ if (nullptr == surface) {
+ jniThrowException(env, IllegalStateException, "Unable to get surface from SurfaceTexture");
+ return;
+ }
+ sp<IGraphicBufferProducer> igbp = surface->getIGraphicBufferProducer();
+ if (nullptr == igbp) {
+ jniThrowException(env, IllegalStateException, "Unable to get IGBP from Surface");
+ return;
+ }
+ SurfaceTexture_setProducer(env, thiz, igbp);
+#else
consumer->setConsumerIsProtected(isProtectedContext());
SurfaceTexture_setSurfaceTexture(env, thiz, surfaceTexture);
SurfaceTexture_setProducer(env, thiz, producer);
-
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
jclass clazz = env->GetObjectClass(thiz);
if (clazz == NULL) {
jniThrowRuntimeException(env,
diff --git a/libs/hwui/tests/common/TestContext.cpp b/libs/hwui/tests/common/TestContext.cpp
index fd596d9..e427c97 100644
--- a/libs/hwui/tests/common/TestContext.cpp
+++ b/libs/hwui/tests/common/TestContext.cpp
@@ -16,6 +16,7 @@
#include "tests/common/TestContext.h"
+#include <com_android_graphics_libgui_flags.h>
#include <cutils/trace.h>
namespace android {
@@ -101,6 +102,14 @@
}
void TestContext::createOffscreenSurface() {
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+ mConsumer = new BufferItemConsumer(GRALLOC_USAGE_HW_COMPOSER, 4);
+ const ui::Size& resolution = getActiveDisplayResolution();
+ mConsumer->setDefaultBufferSize(resolution.getWidth(), resolution.getHeight());
+ mSurface = mConsumer->getSurface();
+ mSurface->setMaxDequeuedBufferCount(3);
+ mSurface->setAsyncMode(true);
+#else
sp<IGraphicBufferProducer> producer;
sp<IGraphicBufferConsumer> consumer;
BufferQueue::createBufferQueue(&producer, &consumer);
@@ -110,6 +119,7 @@
const ui::Size& resolution = getActiveDisplayResolution();
mConsumer->setDefaultBufferSize(resolution.getWidth(), resolution.getHeight());
mSurface = new Surface(producer);
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
}
void TestContext::waitForVsync() {
@@ -144,4 +154,4 @@
} // namespace test
} // namespace uirenderer
-} // namespace android
+} // namespace android
\ No newline at end of file
diff --git a/media/jni/android_media_ImageReader.cpp b/media/jni/android_media_ImageReader.cpp
index 371e3d2..019b1e0 100644
--- a/media/jni/android_media_ImageReader.cpp
+++ b/media/jni/android_media_ImageReader.cpp
@@ -17,35 +17,31 @@
//#define LOG_NDEBUG 0
#define LOG_TAG "ImageReader_JNI"
#define ATRACE_TAG ATRACE_TAG_CAMERA
-#include "android_media_Utils.h"
+#include <android/hardware_buffer_jni.h>
+#include <android_runtime/AndroidRuntime.h>
+#include <android_runtime/android_graphics_GraphicBuffer.h>
+#include <android_runtime/android_hardware_HardwareBuffer.h>
+#include <android_runtime/android_view_Surface.h>
+#include <com_android_graphics_libgui_flags.h>
#include <cutils/atomic.h>
-#include <utils/Log.h>
-#include <utils/misc.h>
+#include <grallocusage/GrallocUsageConversion.h>
+#include <gui/BufferItemConsumer.h>
+#include <gui/Surface.h>
+#include <inttypes.h>
+#include <jni.h>
+#include <nativehelper/JNIHelp.h>
+#include <private/android/AHardwareBufferHelpers.h>
+#include <stdint.h>
+#include <ui/Rect.h>
#include <utils/List.h>
-#include <utils/Trace.h>
+#include <utils/Log.h>
#include <utils/String8.h>
+#include <utils/Trace.h>
+#include <utils/misc.h>
#include <cstdio>
-#include <gui/BufferItemConsumer.h>
-#include <gui/Surface.h>
-
-#include <android_runtime/AndroidRuntime.h>
-#include <android_runtime/android_view_Surface.h>
-#include <android_runtime/android_graphics_GraphicBuffer.h>
-#include <android_runtime/android_hardware_HardwareBuffer.h>
-#include <grallocusage/GrallocUsageConversion.h>
-
-#include <private/android/AHardwareBufferHelpers.h>
-
-#include <jni.h>
-#include <nativehelper/JNIHelp.h>
-
-#include <stdint.h>
-#include <inttypes.h>
-#include <android/hardware_buffer_jni.h>
-
-#include <ui/Rect.h>
+#include "android_media_Utils.h"
#define ANDROID_MEDIA_IMAGEREADER_CTX_JNI_ID "mNativeContext"
#define ANDROID_MEDIA_SURFACEIMAGE_BUFFER_JNI_ID "mNativeBuffer"
@@ -393,18 +389,25 @@
}
sp<JNIImageReaderContext> ctx(new JNIImageReaderContext(env, weakThiz, clazz, maxImages));
- sp<IGraphicBufferProducer> gbProducer;
- sp<IGraphicBufferConsumer> gbConsumer;
- BufferQueue::createBufferQueue(&gbProducer, &gbConsumer);
- sp<BufferItemConsumer> bufferConsumer;
String8 consumerName = String8::format("ImageReader-%dx%df%xm%d-%d-%d",
width, height, nativeHalFormat, maxImages, getpid(),
createProcessUniqueId());
uint64_t consumerUsage =
android_hardware_HardwareBuffer_convertToGrallocUsageBits(ndkUsage);
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+ sp<BufferItemConsumer> bufferConsumer = new BufferItemConsumer(consumerUsage, maxImages,
+ /*controlledByApp*/ true);
+ sp<IGraphicBufferProducer> gbProducer =
+ bufferConsumer->getSurface()->getIGraphicBufferProducer();
+#else
+ sp<IGraphicBufferProducer> gbProducer;
+ sp<IGraphicBufferConsumer> gbConsumer;
+ BufferQueue::createBufferQueue(&gbProducer, &gbConsumer);
+ sp<BufferItemConsumer> bufferConsumer;
bufferConsumer = new BufferItemConsumer(gbConsumer, consumerUsage, maxImages,
/*controlledByApp*/true);
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
if (bufferConsumer == nullptr) {
jniThrowExceptionFmt(env, "java/lang/RuntimeException",
"Failed to allocate native buffer consumer for hal format 0x%x and usage 0x%x",
@@ -413,7 +416,11 @@
}
if (consumerUsage & GRALLOC_USAGE_PROTECTED) {
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+ bufferConsumer->setConsumerIsProtected(true);
+#else
gbConsumer->setConsumerIsProtected(true);
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
}
ctx->setBufferConsumer(bufferConsumer);
diff --git a/media/mca/filterfw/native/core/gl_env.cpp b/media/mca/filterfw/native/core/gl_env.cpp
index 1bb82f8..4637ccd 100644
--- a/media/mca/filterfw/native/core/gl_env.cpp
+++ b/media/mca/filterfw/native/core/gl_env.cpp
@@ -15,21 +15,23 @@
*/
// #define LOG_NDEBUG 0
-#include "base/logging.h"
-#include "base/utilities.h"
#include "core/gl_env.h"
-#include "core/shader_program.h"
-#include "core/vertex_frame.h"
-#include "system/window.h"
+
+#include <EGL/eglext.h>
+#include <com_android_graphics_libgui_flags.h>
+#include <gui/BufferQueue.h>
+#include <gui/GLConsumer.h>
+#include <gui/IGraphicBufferProducer.h>
+#include <gui/Surface.h>
#include <map>
#include <string>
-#include <EGL/eglext.h>
-#include <gui/BufferQueue.h>
-#include <gui/Surface.h>
-#include <gui/GLConsumer.h>
-#include <gui/IGraphicBufferProducer.h>
+#include "base/logging.h"
+#include "base/utilities.h"
+#include "core/shader_program.h"
+#include "core/vertex_frame.h"
+#include "system/window.h"
namespace android {
namespace filterfw {
@@ -165,12 +167,18 @@
}
// Create dummy surface using a GLConsumer
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+ surfaceTexture_ = new GLConsumer(0, GLConsumer::TEXTURE_EXTERNAL, /*useFenceSync=*/true,
+ /*isControlledByApp=*/false);
+ window_ = surfaceTexture_->getSurface();
+#else
sp<IGraphicBufferProducer> producer;
sp<IGraphicBufferConsumer> consumer;
BufferQueue::createBufferQueue(&producer, &consumer);
surfaceTexture_ = new GLConsumer(consumer, 0, GLConsumer::TEXTURE_EXTERNAL,
true, false);
window_ = new Surface(producer);
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
surfaces_[0] = SurfaceWindowPair(eglCreateWindowSurface(display(), config, window_.get(), NULL), NULL);
if (CheckEGLError("eglCreateWindowSurface")) return false;
diff --git a/packages/SettingsLib/src/com/android/settingslib/notification/modes/ZenMode.java b/packages/SettingsLib/src/com/android/settingslib/notification/modes/ZenMode.java
index d36b55f..9fa8fc3 100644
--- a/packages/SettingsLib/src/com/android/settingslib/notification/modes/ZenMode.java
+++ b/packages/SettingsLib/src/com/android/settingslib/notification/modes/ZenMode.java
@@ -286,8 +286,7 @@
/**
* Returns the mode icon -- which can be either app-provided (via {@code addAutomaticZenRule}),
- * user-chosen (via the icon picker in Settings), the app's launcher icon for implicit rules
- * (in its monochrome variant, if available), or a default icon based on the mode type.
+ * user-chosen (via the icon picker in Settings), or a default icon based on the mode type.
*/
@NonNull
public ListenableFuture<Drawable> getIcon(@NonNull Context context,
@@ -300,23 +299,6 @@
return iconLoader.getIcon(context, mRule);
}
- /**
- * Returns an alternative mode icon. The difference with {@link #getIcon} is that it's the
- * basic DND icon not only for Manual DND, but also for <em>implicit rules</em>. As such, it's
- * suitable for places where showing the launcher icon of an app could be confusing, such as
- * the status bar or lockscreen.
- */
- @NonNull
- public ListenableFuture<Drawable> getLockscreenIcon(@NonNull Context context,
- @NonNull ZenIconLoader iconLoader) {
- if (mKind == Kind.MANUAL_DND || mKind == Kind.IMPLICIT) {
- return Futures.immediateFuture(requireNonNull(
- context.getDrawable(R.drawable.ic_do_not_disturb_on_24dp)));
- }
-
- return iconLoader.getIcon(context, mRule);
- }
-
@NonNull
public ZenPolicy getPolicy() {
switch (mRule.getInterruptionFilter()) {
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/modes/ZenModeTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/modes/ZenModeTest.java
index f533e77..32216fa 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/modes/ZenModeTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/modes/ZenModeTest.java
@@ -322,41 +322,6 @@
verify(iconLoader).getIcon(any(), eq(IMPLICIT_ZEN_RULE));
}
- @Test
- public void getLockscreenIcon_normalMode_loadsIconNormally() {
- ZenIconLoader iconLoader = mock(ZenIconLoader.class);
- ZenMode mode = new ZenMode("id", ZEN_RULE, zenConfigRuleFor(ZEN_RULE, false));
-
- ListenableFuture<Drawable> unused = mode.getLockscreenIcon(
- RuntimeEnvironment.getApplication(), iconLoader);
-
- verify(iconLoader).getIcon(any(), eq(ZEN_RULE));
- }
-
- @Test
- public void getLockscreenIcon_manualDnd_returnsFixedIcon() {
- ZenIconLoader iconLoader = mock(ZenIconLoader.class);
-
- ListenableFuture<Drawable> future = TestModeBuilder.MANUAL_DND_INACTIVE.getLockscreenIcon(
- RuntimeEnvironment.getApplication(), iconLoader);
-
- assertThat(future.isDone()).isTrue();
- verify(iconLoader, never()).getIcon(any(), any());
- }
-
- @Test
- public void getLockscreenIcon_implicitMode_returnsFixedIcon() {
- ZenIconLoader iconLoader = mock(ZenIconLoader.class);
- ZenMode mode = new ZenMode(IMPLICIT_RULE_ID, IMPLICIT_ZEN_RULE,
- zenConfigRuleFor(IMPLICIT_ZEN_RULE, false));
-
- ListenableFuture<Drawable> future = mode.getLockscreenIcon(
- RuntimeEnvironment.getApplication(), iconLoader);
-
- assertThat(future.isDone()).isTrue();
- verify(iconLoader, never()).getIcon(any(), any());
- }
-
private static void assertUnparceledIsEqualToOriginal(String type, ZenMode original) {
Parcel parcel = Parcel.obtain();
try {
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDsl.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDsl.kt
index 5cc194d..2b5953c 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDsl.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDsl.kt
@@ -523,8 +523,8 @@
fun convert(progress: Float): Float
companion object {
- /** Keeps scrolling linearly */
- val Default = linear()
+ /** Starts linearly with some resistance and slowly approaches to 0.2f */
+ val Default = tanh(maxProgress = 0.2f, tilt = 3f)
/**
* The scroll stays linear, with [factor] you can control how much resistance there is.
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt
index 682fe95..770c0f8 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt
@@ -724,6 +724,7 @@
layoutHeight = layoutHeight,
sceneTransitions = {
overscroll(SceneB, Orientation.Vertical) {
+ progressConverter = ProgressConverter.linear()
// On overscroll 100% -> Foo should translate by overscrollTranslateY
translate(TestElements.Foo, y = overscrollTranslateY)
}
@@ -780,6 +781,7 @@
transitions =
transitions {
overscroll(SceneB, Orientation.Vertical) {
+ progressConverter = ProgressConverter.linear()
translate(TestElements.Foo, y = overscrollTranslateY)
}
}
@@ -921,6 +923,7 @@
layoutHeight = layoutHeight,
sceneTransitions = {
overscroll(SceneB, Orientation.Vertical) {
+ progressConverter = ProgressConverter.linear()
// On overscroll 100% -> Foo should translate by layoutHeight
translate(TestElements.Foo, y = { absoluteDistance })
}
@@ -1015,7 +1018,7 @@
layoutHeight = layoutHeight,
sceneTransitions = {
// Overscroll progress will be linear (by default)
- defaultOverscrollProgressConverter = ProgressConverter { it }
+ defaultOverscrollProgressConverter = ProgressConverter.linear()
overscroll(SceneB, Orientation.Vertical) {
// This override the defaultOverscrollProgressConverter
@@ -1125,6 +1128,7 @@
)
overscroll(SceneB, Orientation.Vertical) {
+ progressConverter = ProgressConverter.linear()
// On overscroll 100% -> Foo should translate by layoutHeight
translate(TestElements.Foo, y = { absoluteDistance })
}
@@ -1861,6 +1865,7 @@
SceneA,
transitions {
overscroll(SceneB, Orientation.Vertical) {
+ progressConverter = ProgressConverter.linear()
translate(TestElements.Foo, y = 15.dp)
}
}
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt
index 06799bc..e48cd817 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt
@@ -614,6 +614,7 @@
from(SceneA, to = SceneB) { distance = FixedDistance(swipeDistance) }
overscroll(SceneB, Orientation.Vertical) {
+ progressConverter = ProgressConverter.linear()
translate(TestElements.Foo, x = { 20.dp.toPx() }, y = { 30.dp.toPx() })
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/EnRouteViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/EnRouteViewModelTest.kt
new file mode 100644
index 0000000..a310ef4
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/EnRouteViewModelTest.kt
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:OptIn(ExperimentalCoroutinesApi::class)
+
+package com.android.systemui.statusbar.notification.row.ui.viewmodel
+
+import android.platform.test.annotations.EnableFlags
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.statusbar.notification.row.data.repository.fakeNotificationRowRepository
+import com.android.systemui.statusbar.notification.row.shared.EnRouteContentModel
+import com.android.systemui.statusbar.notification.row.shared.IconModel
+import com.android.systemui.statusbar.notification.row.shared.RichOngoingNotificationFlag
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.mock
+
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+@EnableFlags(RichOngoingNotificationFlag.FLAG_NAME)
+class EnRouteViewModelTest : SysuiTestCase() {
+ private val kosmos = testKosmos()
+ private val testScope = kosmos.testScope
+ private val repository = kosmos.fakeNotificationRowRepository
+
+ private var contentModel: EnRouteContentModel?
+ get() = repository.richOngoingContentModel.value as? EnRouteContentModel
+ set(value) {
+ repository.richOngoingContentModel.value = value
+ }
+
+ private lateinit var underTest: EnRouteViewModel
+
+ @Before
+ fun setup() {
+ underTest = kosmos.getEnRouteViewModel(repository)
+ }
+
+ @Test
+ fun viewModelShowsContent() =
+ testScope.runTest {
+ val title by collectLastValue(underTest.title)
+ val text by collectLastValue(underTest.text)
+ contentModel =
+ exampleEnRouteContent(
+ title = "Example EnRoute Title",
+ text = "Example EnRoute Text",
+ )
+ assertThat(title).isEqualTo("Example EnRoute Title")
+ assertThat(text).isEqualTo("Example EnRoute Text")
+ }
+
+ private fun exampleEnRouteContent(
+ icon: IconModel = mock(),
+ title: CharSequence = "example text",
+ text: CharSequence = "example title",
+ ) =
+ EnRouteContentModel(
+ smallIcon = icon,
+ title = title,
+ text = text,
+ )
+}
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/WeatherData.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/WeatherData.kt
index 789a473..f920b18 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/WeatherData.kt
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/WeatherData.kt
@@ -4,6 +4,7 @@
import android.util.Log
import android.view.View
import androidx.annotation.VisibleForTesting
+import androidx.core.text.util.LocalePreferences
typealias WeatherTouchAction = (View) -> Unit
@@ -54,12 +55,35 @@
}
}
- private fun readIntFromBundle(extras: Bundle, key: String): Int? =
+ private fun readIntFromBundle(extras: Bundle, key: String): Int? {
try {
- extras.getString(key)?.toInt()
+ return extras.getString(key)?.toInt()
} catch (e: Exception) {
- null
+ return null
}
+ }
+
+ fun getPlaceholderWeatherData(): WeatherData {
+ return getPlaceholderWeatherData(
+ LocalePreferences.getTemperatureUnit() == LocalePreferences.TemperatureUnit.CELSIUS
+ )
+ }
+
+ private const val DESCRIPTION_PLACEHODLER = ""
+ private const val TEMPERATURE_FAHRENHEIT_PLACEHOLDER = 58
+ private const val TEMPERATURE_CELSIUS_PLACEHOLDER = 21
+ private val WEATHERICON_PLACEHOLDER = WeatherData.WeatherStateIcon.MOSTLY_SUNNY
+
+ fun getPlaceholderWeatherData(useCelsius: Boolean): WeatherData {
+ return WeatherData(
+ description = DESCRIPTION_PLACEHODLER,
+ state = WEATHERICON_PLACEHOLDER,
+ temperature =
+ if (useCelsius) TEMPERATURE_CELSIUS_PLACEHOLDER
+ else TEMPERATURE_FAHRENHEIT_PLACEHOLDER,
+ useCelsius = useCelsius,
+ )
+ }
}
// Values for WeatherStateIcon must stay in sync with go/g3-WeatherStateIcon
diff --git a/packages/SystemUI/res/layout/notification_template_en_route_contracted.xml b/packages/SystemUI/res/layout/notification_template_en_route_contracted.xml
new file mode 100644
index 0000000..59cfecc
--- /dev/null
+++ b/packages/SystemUI/res/layout/notification_template_en_route_contracted.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2024 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<com.android.systemui.statusbar.notification.row.ui.view.EnRouteView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/status_bar_latest_event_content"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:minHeight="@*android:dimen/notification_headerless_min_height"
+ android:tag="enroute"
+ >
+
+ <include layout="@*android:layout/notification_template_material_base" />
+
+</com.android.systemui.statusbar.notification.row.ui.view.EnRouteView>
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
index 5dcf161..c1eae2e 100644
--- a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
+++ b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
@@ -477,6 +477,12 @@
smallClockFrame?.viewTreeObserver?.removeOnGlobalLayoutListener(onGlobalLayoutListener)
}
+ fun setFallbackWeatherData(data: WeatherData) {
+ if (weatherData != null) return
+ weatherData = data
+ clock?.run { events.onWeatherDataChanged(data) }
+ }
+
/**
* Sets this clock as showing in a secondary display.
*
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
index 51ce355..9ccfb50 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
@@ -79,6 +79,7 @@
import com.android.systemui.monet.ColorScheme
import com.android.systemui.monet.Style
import com.android.systemui.plugins.clocks.ClockController
+import com.android.systemui.plugins.clocks.WeatherData
import com.android.systemui.res.R
import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.shared.clocks.ClockRegistry
@@ -188,6 +189,7 @@
init {
coroutineScope = CoroutineScope(applicationScope.coroutineContext + Job())
disposables += DisposableHandle { coroutineScope.cancel() }
+ clockController.setFallbackWeatherData(WeatherData.getPlaceholderWeatherData())
if (KeyguardBottomAreaRefactor.isEnabled) {
quickAffordancesCombinedViewModel.enablePreviewMode(
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractor.kt
index 5f5b265..3f18fc2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractor.kt
@@ -57,7 +57,7 @@
isActivated = modes.isNotEmpty(),
icon =
if (Flags.modesApi() && Flags.modesUi() && Flags.modesUiIcons())
- zenModeInteractor.getActiveModeIcon(context, modes)
+ zenModeInteractor.getActiveModeIcon(modes)
else null,
activeModes = modes.map { it.name }
)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinator.kt
index f74c9a6..e9292f8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinator.kt
@@ -79,6 +79,7 @@
// NOTE: NotificationEntry.isClearable will internally check group children to ensure
// the group itself definitively clearable.
val isClearable = !isSensitiveContentProtectionActive && entry.isClearable
+ && !entry.isSensitive.value
when {
isSilent && isClearable -> hasClearableSilentNotifs = true
isSilent && !isClearable -> hasNonClearableSilentNotifs = true
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RichOngoingNotificationContentExtractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RichOngoingNotificationContentExtractor.kt
index bf5b3a3..da29b0f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RichOngoingNotificationContentExtractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RichOngoingNotificationContentExtractor.kt
@@ -22,6 +22,7 @@
import android.util.Log
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.row.shared.EnRouteContentModel
import com.android.systemui.statusbar.notification.row.shared.IconModel
import com.android.systemui.statusbar.notification.row.shared.RichOngoingContentModel
import com.android.systemui.statusbar.notification.row.shared.RichOngoingNotificationFlag
@@ -68,12 +69,13 @@
builder: Notification.Builder,
systemUIContext: Context,
packageContext: Context
- ): RichOngoingContentModel? =
+ ): RichOngoingContentModel? {
+ val sbn = entry.sbn
+ val notification = sbn.notification
+ val icon = IconModel(notification.smallIcon)
+
try {
- val sbn = entry.sbn
- val notification = sbn.notification
- val icon = IconModel(notification.smallIcon)
- if (sbn.packageName == "com.google.android.deskclock") {
+ return if (sbn.packageName == "com.google.android.deskclock") {
when (notification.channelId) {
"Timers v2" -> {
parseTimerNotification(notification, icon)
@@ -87,11 +89,14 @@
null
}
}
+ } else if (builder.style is Notification.EnRouteStyle) {
+ parseEnRouteNotification(notification, icon)
} else null
} catch (e: Exception) {
Log.e("RONs", "Error parsing RON", e)
- null
+ return null
}
+ }
/**
* FOR PROTOTYPING ONLY: create a RON TimerContentModel using the time information available
@@ -199,4 +204,15 @@
.plusMinutes(minute.toLong())
.plusSeconds(second.toLong())
}
+
+ private fun parseEnRouteNotification(
+ notification: Notification,
+ icon: IconModel,
+ ): EnRouteContentModel {
+ return EnRouteContentModel(
+ smallIcon = icon,
+ title = notification.extras.getCharSequence(Notification.EXTRA_TITLE),
+ text = notification.extras.getCharSequence(Notification.EXTRA_TEXT),
+ )
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RichOngoingNotificationViewInflater.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RichOngoingNotificationViewInflater.kt
index 828fc21..2c462b7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RichOngoingNotificationViewInflater.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RichOngoingNotificationViewInflater.kt
@@ -27,12 +27,15 @@
import com.android.systemui.statusbar.notification.row.ContentViewInflationResult.InflatedContentViewHolder
import com.android.systemui.statusbar.notification.row.ContentViewInflationResult.KeepExistingView
import com.android.systemui.statusbar.notification.row.ContentViewInflationResult.NullContentView
+import com.android.systemui.statusbar.notification.row.shared.EnRouteContentModel
import com.android.systemui.statusbar.notification.row.shared.RichOngoingContentModel
import com.android.systemui.statusbar.notification.row.shared.RichOngoingNotificationFlag
-import com.android.systemui.statusbar.notification.row.shared.StopwatchContentModel
import com.android.systemui.statusbar.notification.row.shared.TimerContentModel
+import com.android.systemui.statusbar.notification.row.ui.view.EnRouteView
import com.android.systemui.statusbar.notification.row.ui.view.TimerView
+import com.android.systemui.statusbar.notification.row.ui.viewbinder.EnRouteViewBinder
import com.android.systemui.statusbar.notification.row.ui.viewbinder.TimerViewBinder
+import com.android.systemui.statusbar.notification.row.ui.viewmodel.EnRouteViewModel
import com.android.systemui.statusbar.notification.row.ui.viewmodel.RichOngoingViewModelComponent
import com.android.systemui.statusbar.notification.row.ui.viewmodel.TimerViewModel
import javax.inject.Inject
@@ -119,7 +122,15 @@
parentView,
viewType
)
- is StopwatchContentModel -> TODO("Not yet implemented")
+ is EnRouteContentModel ->
+ inflateEnRouteView(
+ existingView,
+ component::createEnRouteViewModel,
+ systemUiContext,
+ parentView,
+ viewType
+ )
+ else -> TODO("Not yet implemented")
}
}
@@ -131,7 +142,8 @@
if (RichOngoingNotificationFlag.isUnexpectedlyInLegacyMode()) return false
return when (contentModel) {
is TimerContentModel -> canKeepTimerView(contentModel, existingView, viewType)
- is StopwatchContentModel -> TODO("Not yet implemented")
+ is EnRouteContentModel -> canKeepEnRouteView(contentModel, existingView, viewType)
+ else -> TODO("Not yet implemented")
}
}
@@ -167,4 +179,38 @@
existingView: View?,
viewType: RichOngoingNotificationViewType
): Boolean = true
+
+ private fun inflateEnRouteView(
+ existingView: View?,
+ createViewModel: () -> EnRouteViewModel,
+ systemUiContext: Context,
+ parentView: ViewGroup,
+ viewType: RichOngoingNotificationViewType,
+ ): ContentViewInflationResult {
+ if (existingView is EnRouteView && !existingView.isReinflateNeeded())
+ return KeepExistingView
+ return when (viewType) {
+ RichOngoingNotificationViewType.Contracted -> {
+ val newView =
+ LayoutInflater.from(systemUiContext)
+ .inflate(
+ R.layout.notification_template_en_route_contracted,
+ parentView,
+ /* attachToRoot= */ false
+ ) as EnRouteView
+
+ InflatedContentViewHolder(newView) {
+ EnRouteViewBinder.bindWhileAttached(newView, createViewModel())
+ }
+ }
+ RichOngoingNotificationViewType.Expanded,
+ RichOngoingNotificationViewType.HeadsUp -> NullContentView
+ }
+ }
+
+ private fun canKeepEnRouteView(
+ contentModel: EnRouteContentModel,
+ existingView: View?,
+ viewType: RichOngoingNotificationViewType
+ ): Boolean = true
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/domain/interactor/NotificationRowInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/domain/interactor/NotificationRowInteractor.kt
index 4705ace..72823a7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/domain/interactor/NotificationRowInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/domain/interactor/NotificationRowInteractor.kt
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.notification.row.domain.interactor
import com.android.systemui.statusbar.notification.row.data.repository.NotificationRowRepository
+import com.android.systemui.statusbar.notification.row.shared.EnRouteContentModel
import com.android.systemui.statusbar.notification.row.shared.TimerContentModel
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
@@ -26,4 +27,8 @@
/** Content of a rich ongoing timer notification. */
val timerContentModel: Flow<TimerContentModel> =
repository.richOngoingContentModel.filterIsInstance<TimerContentModel>()
+
+ /** Content of a rich ongoing timer notification. */
+ val enRouteContentModel: Flow<EnRouteContentModel> =
+ repository.richOngoingContentModel.filterIsInstance<EnRouteContentModel>()
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/shared/EnRouteContentModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/shared/EnRouteContentModel.kt
new file mode 100644
index 0000000..7e78cca
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/shared/EnRouteContentModel.kt
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.row.shared
+
+/**
+ * Represents something en route.
+ *
+ * @param smallIcon the main small icon of the EnRoute notification.
+ * @param title the title of the EnRoute notification.
+ * @param text the text of the EnRoute notification.
+ */
+data class EnRouteContentModel(
+ val smallIcon: IconModel,
+ val title: CharSequence?,
+ val text: CharSequence?,
+) : RichOngoingContentModel
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/view/EnRouteView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/view/EnRouteView.kt
new file mode 100644
index 0000000..e5c2b5f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/view/EnRouteView.kt
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.row.ui.view
+
+import android.content.Context
+import android.graphics.drawable.Icon
+import android.util.AttributeSet
+import android.widget.FrameLayout
+import android.widget.ImageView
+import android.widget.TextView
+import com.android.internal.R
+import com.android.internal.widget.NotificationExpandButton
+
+class EnRouteView
+@JvmOverloads
+constructor(
+ context: Context,
+ attrs: AttributeSet? = null,
+ defStyleAttr: Int = 0,
+ defStyleRes: Int = 0,
+) : FrameLayout(context, attrs, defStyleAttr, defStyleRes) {
+
+ private val configTracker = ConfigurationTracker(resources)
+
+ private lateinit var icon: ImageView
+ private lateinit var title: TextView
+ private lateinit var text: TextView
+ private lateinit var expandButton: NotificationExpandButton
+
+ override fun onFinishInflate() {
+ super.onFinishInflate()
+ icon = requireViewById(R.id.icon)
+ title = requireViewById(R.id.title)
+ text = requireViewById(R.id.text)
+
+ expandButton = requireViewById(R.id.expand_button)
+ expandButton.setExpanded(false)
+ }
+
+ /** the resources configuration has changed such that the view needs to be reinflated */
+ fun isReinflateNeeded(): Boolean = configTracker.hasUnhandledConfigChange()
+
+ fun setIcon(icon: Icon?) {
+ this.icon.setImageIcon(icon)
+ }
+
+ fun setTitle(title: CharSequence?) {
+ this.title.text = title
+ }
+
+ fun setText(text: CharSequence?) {
+ this.text.text = text
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewbinder/EnRouteViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewbinder/EnRouteViewBinder.kt
new file mode 100644
index 0000000..3b8957c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewbinder/EnRouteViewBinder.kt
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.row.ui.viewbinder
+
+import androidx.lifecycle.lifecycleScope
+import com.android.systemui.lifecycle.repeatWhenAttached
+import com.android.systemui.statusbar.notification.row.ui.view.EnRouteView
+import com.android.systemui.statusbar.notification.row.ui.viewmodel.EnRouteViewModel
+import kotlinx.coroutines.DisposableHandle
+import kotlinx.coroutines.coroutineScope
+import kotlinx.coroutines.launch
+
+/** Binds a [EnRouteView] to its [view model][EnRouteViewModel]. */
+object EnRouteViewBinder {
+ fun bindWhileAttached(
+ view: EnRouteView,
+ viewModel: EnRouteViewModel,
+ ): DisposableHandle {
+ return view.repeatWhenAttached { lifecycleScope.launch { bind(view, viewModel) } }
+ }
+
+ suspend fun bind(
+ view: EnRouteView,
+ viewModel: EnRouteViewModel,
+ ) = coroutineScope {
+ launch { viewModel.icon.collect { view.setIcon(it) } }
+ launch { viewModel.title.collect { view.setTitle(it) } }
+ launch { viewModel.text.collect { view.setText(it) } }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/EnRouteViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/EnRouteViewModel.kt
new file mode 100644
index 0000000..307a983
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/EnRouteViewModel.kt
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.row.ui.viewmodel
+
+import android.graphics.drawable.Icon
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.statusbar.notification.row.domain.interactor.NotificationRowInteractor
+import com.android.systemui.statusbar.notification.row.shared.RichOngoingNotificationFlag
+import com.android.systemui.util.kotlin.FlowDumperImpl
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.mapNotNull
+
+/** A view model for EnRoute notifications. */
+class EnRouteViewModel
+@Inject
+constructor(
+ dumpManager: DumpManager,
+ rowInteractor: NotificationRowInteractor,
+) : FlowDumperImpl(dumpManager) {
+ init {
+ /* check if */ RichOngoingNotificationFlag.isUnexpectedlyInLegacyMode()
+ }
+
+ val icon: Flow<Icon?> = rowInteractor.enRouteContentModel.mapNotNull { it.smallIcon.icon }
+
+ val title: Flow<CharSequence?> = rowInteractor.enRouteContentModel.map { it.title }
+
+ val text: Flow<CharSequence?> = rowInteractor.enRouteContentModel.map { it.text }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/RichOngoingViewModelComponent.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/RichOngoingViewModelComponent.kt
index dad52a3..5552d89 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/RichOngoingViewModelComponent.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/RichOngoingViewModelComponent.kt
@@ -33,4 +33,6 @@
}
fun createTimerViewModel(): TimerViewModel
+
+ fun createEnRouteViewModel(): EnRouteViewModel
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt
index 0e984cf..f9937e5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt
@@ -17,7 +17,9 @@
package com.android.systemui.statusbar.notification.stack.ui.viewmodel
-import com.android.compose.animation.scene.ObservableTransitionState
+import com.android.compose.animation.scene.ObservableTransitionState.Idle
+import com.android.compose.animation.scene.ObservableTransitionState.Transition
+import com.android.compose.animation.scene.ObservableTransitionState.Transition.ChangeCurrentScene
import com.android.compose.animation.scene.SceneKey
import com.android.systemui.dump.DumpManager
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
@@ -66,36 +68,40 @@
activateFlowDumper()
}
- private fun expandFractionForScene(scene: SceneKey, shadeExpansion: Float): Float =
- when (scene) {
+ private fun expandedInScene(scene: SceneKey): Boolean {
+ return when (scene) {
Scenes.Lockscreen,
- Scenes.QuickSettings -> 1f
- else -> shadeExpansion
+ Scenes.Shade,
+ Scenes.QuickSettings -> true
+ else -> false
}
+ }
- private fun expandFractionForTransition(
- state: ObservableTransitionState.Transition.ChangeCurrentScene,
+ private fun fullyExpandedDuringSceneChange(change: ChangeCurrentScene): Boolean {
+ // The lockscreen stack is visible during all transitions away from the lockscreen, so keep
+ // the stack expanded until those transitions finish.
+ return (expandedInScene(change.fromScene) && expandedInScene(change.toScene)) ||
+ change.isBetween({ it == Scenes.Lockscreen }, { true })
+ }
+
+ private fun expandFractionDuringSceneChange(
+ change: ChangeCurrentScene,
shadeExpansion: Float,
- shadeMode: ShadeMode,
qsExpansion: Float,
- quickSettingsScene: SceneKey
- ): Float =
- if (
- state.isBetween({ it == Scenes.Lockscreen }, { it in SceneFamilies.NotifShade }) ||
- state.isBetween({ it in SceneFamilies.NotifShade }, { it == quickSettingsScene })
- ) {
+ ): Float {
+ return if (fullyExpandedDuringSceneChange(change)) {
1f
- } else if (
- shadeMode != ShadeMode.Split &&
- state.isBetween({ it in SceneFamilies.Home }, { it == quickSettingsScene })
- ) {
+ } else if (change.isBetween({ it == Scenes.Gone }, { it in SceneFamilies.NotifShade })) {
+ shadeExpansion
+ } else if (change.isBetween({ it == Scenes.Gone }, { it == Scenes.QuickSettings })) {
// during QS expansion, increase fraction at same rate as scrim alpha,
// but start when scrim alpha is at EXPANSION_FOR_DELAYED_STACK_FADE_IN.
(qsExpansion / EXPANSION_FOR_MAX_SCRIM_ALPHA - EXPANSION_FOR_DELAYED_STACK_FADE_IN)
.coerceIn(0f, 1f)
} else {
- shadeExpansion
+ 0f
}
+ }
/**
* The expansion fraction of the notification stack. It should go from 0 to 1 when transitioning
@@ -109,21 +115,17 @@
shadeInteractor.qsExpansion,
sceneInteractor.transitionState,
sceneInteractor.resolveSceneFamily(SceneFamilies.QuickSettings),
- ) { shadeExpansion, shadeMode, qsExpansion, transitionState, quickSettingsScene ->
+ ) { shadeExpansion, _, qsExpansion, transitionState, _ ->
when (transitionState) {
- is ObservableTransitionState.Idle ->
- expandFractionForScene(transitionState.currentScene, shadeExpansion)
- is ObservableTransitionState.Transition.ChangeCurrentScene ->
- expandFractionForTransition(
+ is Idle -> if (expandedInScene(transitionState.currentScene)) 1f else 0f
+ is ChangeCurrentScene ->
+ expandFractionDuringSceneChange(
transitionState,
shadeExpansion,
- shadeMode,
qsExpansion,
- quickSettingsScene
)
- is ObservableTransitionState.Transition.ShowOrHideOverlay,
- is ObservableTransitionState.Transition.ReplaceOverlay ->
- TODO("b/359173565: Handle overlay transitions")
+ is Transition.ShowOrHideOverlay,
+ is Transition.ReplaceOverlay -> TODO("b/359173565: Handle overlay transitions")
}
}
.distinctUntilChanged()
@@ -134,9 +136,7 @@
val shouldResetStackTop: Flow<Boolean> =
sceneInteractor.transitionState
- .mapNotNull { state ->
- state is ObservableTransitionState.Idle && state.currentScene == Scenes.Gone
- }
+ .mapNotNull { state -> state is Idle && state.currentScene == Scenes.Gone }
.distinctUntilChanged()
.dumpWhileCollecting("shouldResetStackTop")
@@ -241,7 +241,7 @@
}
}
-private fun ObservableTransitionState.Transition.ChangeCurrentScene.isBetween(
+private fun ChangeCurrentScene.isBetween(
a: (SceneKey) -> Boolean,
b: (SceneKey) -> Boolean
): Boolean = (a(fromScene) && b(toScene)) || (b(fromScene) && a(toScene))
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
index 3dd265b..e3242d1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -2365,9 +2365,14 @@
// lock screen where users can use the UDFPS affordance to enter the device
mStatusBarKeyguardViewManager.reset(true);
} else if (mState == StatusBarState.KEYGUARD
- && !mStatusBarKeyguardViewManager.primaryBouncerIsOrWillBeShowing()
- && mStatusBarKeyguardViewManager.isSecure()) {
- if (!relockWithPowerButtonImmediately()) {
+ && !mStatusBarKeyguardViewManager.primaryBouncerIsOrWillBeShowing()) {
+ boolean needsBouncer = mStatusBarKeyguardViewManager.isSecure();
+ if (relockWithPowerButtonImmediately()) {
+ // Only request if SIM bouncer is needed
+ needsBouncer = mStatusBarKeyguardViewManager.needsFullscreenBouncer();
+ }
+
+ if (needsBouncer) {
Log.d(TAG, "showBouncerOrLockScreenIfKeyguard, showingBouncer");
if (SceneContainerFlag.isEnabled()) {
mStatusBarKeyguardViewManager.showPrimaryBouncer(true /* scrimmed */);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractor.kt
index f16fcb5..d351da6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractor.kt
@@ -95,10 +95,6 @@
return mode.getIcon(context, iconLoader).await().asIcon()
}
- suspend fun getLockscreenModeIcon(mode: ZenMode): Icon {
- return mode.getLockscreenIcon(context, iconLoader).await().asIcon()
- }
-
/**
* Given the list of modes (which may include zero or more currently active modes), returns an
* icon representing the active mode, if any (or, if multiple modes are active, to the most
@@ -106,8 +102,8 @@
* standard DND icon for implicit modes, instead of the launcher icon of the associated
* package).
*/
- suspend fun getActiveModeIcon(context: Context, modes: List<ZenMode>): Icon? {
- return getMainActiveMode(modes)?.let { m -> getLockscreenModeIcon(m) }
+ suspend fun getActiveModeIcon(modes: List<ZenMode>): Icon? {
+ return getMainActiveMode(modes)?.let { m -> getModeIcon(m) }
}
fun activateMode(zenMode: ZenMode) {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/lifecycle/ActivatableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/lifecycle/ActivatableTest.kt
similarity index 100%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/lifecycle/ActivatableTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/lifecycle/ActivatableTest.kt
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/lifecycle/SysUiViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/lifecycle/SysUiViewModelTest.kt
similarity index 100%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/lifecycle/SysUiViewModelTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/lifecycle/SysUiViewModelTest.kt
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/compose/DragAndDropTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/DragAndDropTest.kt
similarity index 100%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/compose/DragAndDropTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/DragAndDropTest.kt
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImplTest.kt
similarity index 100%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImplTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImplTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinatorTest.kt
index ad6aca1..3c583f2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinatorTest.kt
@@ -34,6 +34,7 @@
import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor
import com.android.systemui.statusbar.notification.domain.interactor.RenderNotificationListInteractor
import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
import com.android.systemui.statusbar.notification.stack.BUCKET_ALERTING
import com.android.systemui.statusbar.notification.stack.BUCKET_SILENT
import com.android.systemui.statusbar.policy.SensitiveNotificationProtectionController
@@ -45,8 +46,8 @@
import org.mockito.Mock
import org.mockito.Mockito.verify
import org.mockito.Mockito.verifyZeroInteractions
-import org.mockito.Mockito.`when` as whenever
import org.mockito.MockitoAnnotations.initMocks
+import org.mockito.Mockito.`when` as whenever
@SmallTest
@RunWith(AndroidJUnit4::class)
@@ -66,6 +67,7 @@
SensitiveNotificationProtectionController
@Mock private lateinit var stackController: NotifStackController
@Mock private lateinit var section: NotifSection
+ @Mock private lateinit var row: ExpandableNotificationRow
@Before
fun setUp() {
@@ -74,6 +76,8 @@
whenever(sensitiveNotificationProtectionController.isSensitiveStateActive).thenReturn(false)
entry = NotificationEntryBuilder().setSection(section).build()
+ entry.row = row
+ entry.setSensitive(false, false)
coordinator =
StackCoordinator(
groupExpansionManagerImpl,
@@ -189,4 +193,17 @@
.setNotifStats(NotifStats(1, false, false, true, false))
verifyZeroInteractions(stackController)
}
+
+ @Test
+ @EnableFlags(
+ FooterViewRefactor.FLAG_NAME
+ )
+ fun testSetNotificationStats_footerFlagOn_nonClearableRedacted() {
+ entry.setSensitive(true, true)
+ whenever(section.bucket).thenReturn(BUCKET_ALERTING)
+ afterRenderListListener.onAfterRenderList(listOf(entry), stackController)
+ verify(activeNotificationsInteractor)
+ .setNotifStats(NotifStats(1, hasNonClearableAlertingNotifs = true, false, false, false))
+ verifyZeroInteractions(stackController)
+ }
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/EnRouteViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/EnRouteViewModelKosmos.kt
new file mode 100644
index 0000000..7e51135
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/EnRouteViewModelKosmos.kt
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.row.ui.viewmodel
+
+import com.android.systemui.dump.dumpManager
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.statusbar.notification.row.data.repository.NotificationRowRepository
+import com.android.systemui.statusbar.notification.row.domain.interactor.getNotificationRowInteractor
+
+fun Kosmos.getEnRouteViewModel(repository: NotificationRowRepository) =
+ EnRouteViewModel(
+ dumpManager = dumpManager,
+ rowInteractor = getNotificationRowInteractor(repository),
+ )
diff --git a/ravenwood/Android.bp b/ravenwood/Android.bp
index 58cd2e4..c71b99f 100644
--- a/ravenwood/Android.bp
+++ b/ravenwood/Android.bp
@@ -331,6 +331,7 @@
name: "ravenwood-runtime",
data: [
"framework-res",
+ "ravenwood-empty-res",
],
libs: [
"100-framework-minus-apex.ravenwood",
diff --git a/ravenwood/TEST_MAPPING b/ravenwood/TEST_MAPPING
index 691d06e..7e2ee3e 100644
--- a/ravenwood/TEST_MAPPING
+++ b/ravenwood/TEST_MAPPING
@@ -52,10 +52,6 @@
"host": true
},
{
- "name": "RavenwoodCoreTest",
- "host": true
- },
- {
"name": "RavenwoodResApkTest",
"host": true
},
diff --git a/ravenwood/coretest/Android.bp b/ravenwood/coretest/Android.bp
deleted file mode 100644
index a78c5c1..0000000
--- a/ravenwood/coretest/Android.bp
+++ /dev/null
@@ -1,23 +0,0 @@
-package {
- // See: http://go/android-license-faq
- // A large-scale-change added 'default_applicable_licenses' to import
- // all of the 'license_kinds' from "frameworks_base_license"
- // to get the below license kinds:
- // SPDX-license-identifier-Apache-2.0
- default_applicable_licenses: ["frameworks_base_license"],
-}
-
-android_ravenwood_test {
- name: "RavenwoodCoreTest",
-
- static_libs: [
- "androidx.annotation_annotation",
- "androidx.test.ext.junit",
- "androidx.test.rules",
- ],
- srcs: [
- "test/**/*.java",
- ],
- sdk_version: "test_current",
- auto_gen_config: true,
-}
diff --git a/ravenwood/coretest/README.md b/ravenwood/coretest/README.md
deleted file mode 100644
index b60bfbf..0000000
--- a/ravenwood/coretest/README.md
+++ /dev/null
@@ -1,3 +0,0 @@
-# Ravenwood core test
-
-This test contains (non-bivalent) tests for Ravenwood itself -- e.g. tests for the ravenwood rules.
\ No newline at end of file
diff --git a/ravenwood/coretest/test/com/android/ravenwoodtest/coretest/RavenwoodTestRunnerValidationTest.java b/ravenwood/coretest/test/com/android/ravenwoodtest/coretest/RavenwoodTestRunnerValidationTest.java
deleted file mode 100644
index f1e33cb..0000000
--- a/ravenwood/coretest/test/com/android/ravenwoodtest/coretest/RavenwoodTestRunnerValidationTest.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.ravenwoodtest.coretest;
-
-import android.platform.test.ravenwood.RavenwoodRule;
-
-import androidx.test.runner.AndroidJUnit4; // Intentionally use the deprecated one.
-
-import org.junit.Assume;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.ExpectedException;
-import org.junit.rules.RuleChain;
-import org.junit.runner.RunWith;
-
-/**
- * Test for the test runner validator in RavenwoodRule.
- */
-@RunWith(AndroidJUnit4.class)
-public class RavenwoodTestRunnerValidationTest {
- // Note the following rules don't have a @Rule, because they need to be applied in a specific
- // order. So we use a RuleChain instead.
- private ExpectedException mThrown = ExpectedException.none();
- private final RavenwoodRule mRavenwood = new RavenwoodRule();
-
- @Rule
- public final RuleChain chain = RuleChain.outerRule(mThrown).around(mRavenwood);
-
- public RavenwoodTestRunnerValidationTest() {
- Assume.assumeTrue(RavenwoodRule._$RavenwoodPrivate.isOptionalValidationEnabled());
- // Because RavenwoodRule will throw this error before executing the test method,
- // we can't do it in the test method itself.
- // So instead, we initialize it here.
- mThrown.expectMessage("Switch to androidx.test.ext.junit.runners.AndroidJUnit4");
- }
-
- @Test
- public void testValidateTestRunner() {
- }
-}
diff --git a/ravenwood/coretest/test/com/android/ravenwoodtest/coretest/methodvalidation/RavenwoodTestMethodValidation_Fail01_Test.java b/ravenwood/coretest/test/com/android/ravenwoodtest/coretest/methodvalidation/RavenwoodTestMethodValidation_Fail01_Test.java
deleted file mode 100644
index db95fad..0000000
--- a/ravenwood/coretest/test/com/android/ravenwoodtest/coretest/methodvalidation/RavenwoodTestMethodValidation_Fail01_Test.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.ravenwoodtest.coretest.methodvalidation;
-
-import android.platform.test.ravenwood.RavenwoodRule;
-
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.ExpectedException;
-import org.junit.rules.RuleChain;
-import org.junit.runner.RunWith;
-
-/**
- * RavenwoodRule has a validator to ensure "test-looking" methods have valid JUnit annotations.
- * This class contains tests for this validator.
- */
-@RunWith(AndroidJUnit4.class)
-public class RavenwoodTestMethodValidation_Fail01_Test {
- private ExpectedException mThrown = ExpectedException.none();
- private final RavenwoodRule mRavenwood = new RavenwoodRule();
-
- @Rule
- public final RuleChain chain = RuleChain.outerRule(mThrown).around(mRavenwood);
-
- public RavenwoodTestMethodValidation_Fail01_Test() {
- mThrown.expectMessage("Method setUp() doesn't have @Before");
- }
-
- @SuppressWarnings("JUnit4SetUpNotRun")
- public void setUp() {
- }
-
- @Test
- public void testEmpty() {
- }
-}
diff --git a/ravenwood/coretest/test/com/android/ravenwoodtest/coretest/methodvalidation/RavenwoodTestMethodValidation_Fail02_Test.java b/ravenwood/coretest/test/com/android/ravenwoodtest/coretest/methodvalidation/RavenwoodTestMethodValidation_Fail02_Test.java
deleted file mode 100644
index ddc66c7..0000000
--- a/ravenwood/coretest/test/com/android/ravenwoodtest/coretest/methodvalidation/RavenwoodTestMethodValidation_Fail02_Test.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.ravenwoodtest.coretest.methodvalidation;
-
-import android.platform.test.ravenwood.RavenwoodRule;
-
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.ExpectedException;
-import org.junit.rules.RuleChain;
-import org.junit.runner.RunWith;
-
-/**
- * RavenwoodRule has a validator to ensure "test-looking" methods have valid JUnit annotations.
- * This class contains tests for this validator.
- */
-@RunWith(AndroidJUnit4.class)
-public class RavenwoodTestMethodValidation_Fail02_Test {
- private ExpectedException mThrown = ExpectedException.none();
- private final RavenwoodRule mRavenwood = new RavenwoodRule();
-
- @Rule
- public final RuleChain chain = RuleChain.outerRule(mThrown).around(mRavenwood);
-
- public RavenwoodTestMethodValidation_Fail02_Test() {
- mThrown.expectMessage("Method tearDown() doesn't have @After");
- }
-
- @SuppressWarnings("JUnit4TearDownNotRun")
- public void tearDown() {
- }
-
- @Test
- public void testEmpty() {
- }
-}
diff --git a/ravenwood/coretest/test/com/android/ravenwoodtest/coretest/methodvalidation/RavenwoodTestMethodValidation_Fail03_Test.java b/ravenwood/coretest/test/com/android/ravenwoodtest/coretest/methodvalidation/RavenwoodTestMethodValidation_Fail03_Test.java
deleted file mode 100644
index ec8e907..0000000
--- a/ravenwood/coretest/test/com/android/ravenwoodtest/coretest/methodvalidation/RavenwoodTestMethodValidation_Fail03_Test.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.ravenwoodtest.coretest.methodvalidation;
-
-import android.platform.test.ravenwood.RavenwoodRule;
-
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.ExpectedException;
-import org.junit.rules.RuleChain;
-import org.junit.runner.RunWith;
-
-/**
- * RavenwoodRule has a validator to ensure "test-looking" methods have valid JUnit annotations.
- * This class contains tests for this validator.
- */
-@RunWith(AndroidJUnit4.class)
-public class RavenwoodTestMethodValidation_Fail03_Test {
- private ExpectedException mThrown = ExpectedException.none();
- private final RavenwoodRule mRavenwood = new RavenwoodRule();
-
- @Rule
- public final RuleChain chain = RuleChain.outerRule(mThrown).around(mRavenwood);
-
- public RavenwoodTestMethodValidation_Fail03_Test() {
- mThrown.expectMessage("Method testFoo() doesn't have @Test");
- }
-
- @SuppressWarnings("JUnit4TestNotRun")
- public void testFoo() {
- }
-
- @Test
- public void testEmpty() {
- }
-}
diff --git a/ravenwood/coretest/test/com/android/ravenwoodtest/coretest/methodvalidation/RavenwoodTestMethodValidation_OkTest.java b/ravenwood/coretest/test/com/android/ravenwoodtest/coretest/methodvalidation/RavenwoodTestMethodValidation_OkTest.java
deleted file mode 100644
index d952d07..0000000
--- a/ravenwood/coretest/test/com/android/ravenwoodtest/coretest/methodvalidation/RavenwoodTestMethodValidation_OkTest.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.ravenwoodtest.coretest.methodvalidation;
-
-import android.platform.test.ravenwood.RavenwoodRule;
-
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-/**
- * RavenwoodRule has a validator to ensure "test-looking" methods have valid JUnit annotations.
- * This class contains tests for this validator.
- */
-@RunWith(AndroidJUnit4.class)
-public class RavenwoodTestMethodValidation_OkTest {
- @Rule
- public final RavenwoodRule mRavenwood = new RavenwoodRule();
-
- @Before
- public void setUp() {
- }
-
- @Before
- public void testSetUp() {
- }
-
- @After
- public void tearDown() {
- }
-
- @After
- public void testTearDown() {
- }
-
- @Test
- public void testEmpty() {
- }
-}
diff --git a/ravenwood/empty-res/Android.bp b/ravenwood/empty-res/Android.bp
new file mode 100644
index 0000000..3af7690
--- /dev/null
+++ b/ravenwood/empty-res/Android.bp
@@ -0,0 +1,4 @@
+android_app {
+ name: "ravenwood-empty-res",
+ sdk_version: "current",
+}
diff --git a/ravenwood/empty-res/AndroidManifest.xml b/ravenwood/empty-res/AndroidManifest.xml
new file mode 100644
index 0000000..f73460b
--- /dev/null
+++ b/ravenwood/empty-res/AndroidManifest.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2024 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.ravenwood.emptyres">
+</manifest>
diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java
index 3ea4cb7..7b4c173 100644
--- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java
@@ -16,9 +16,9 @@
package android.platform.test.ravenwood;
+import static com.android.ravenwood.common.RavenwoodCommonUtils.RAVENWOOD_EMPTY_RESOURCES_APK;
import static com.android.ravenwood.common.RavenwoodCommonUtils.RAVENWOOD_RESOURCE_APK;
-import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
@@ -39,24 +39,12 @@
import com.android.internal.os.RuntimeInit;
import com.android.server.LocalServices;
-import org.junit.After;
-import org.junit.AfterClass;
-import org.junit.Assert;
-import org.junit.Before;
-import org.junit.BeforeClass;
-import org.junit.Test;
import org.junit.runner.Description;
-import org.junit.runner.RunWith;
import org.junit.runners.model.Statement;
import java.io.File;
import java.io.IOException;
import java.io.PrintStream;
-import java.lang.annotation.Annotation;
-import java.lang.reflect.Method;
-import java.lang.reflect.Modifier;
-import java.util.ArrayList;
-import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.Executors;
@@ -109,10 +97,6 @@
android.os.Process.init$ravenwood(rule.mUid, rule.mPid);
android.os.Binder.init$ravenwood();
-// android.os.SystemProperties.init$ravenwood(
-// rule.mSystemProperties.getValues(),
-// rule.mSystemProperties.getKeyReadablePredicate(),
-// rule.mSystemProperties.getKeyWritablePredicate());
setSystemProperties(rule.mSystemProperties);
ServiceManager.init$ravenwood();
@@ -131,11 +115,12 @@
// TODO This should be integrated into LoadedApk
final Supplier<Resources> resourcesSupplier = () -> {
- final var resApkFile = new File(RAVENWOOD_RESOURCE_APK).getAbsoluteFile();
+ var resApkFile = new File(RAVENWOOD_RESOURCE_APK);
+ if (!resApkFile.isFile()) {
+ resApkFile = new File(RAVENWOOD_EMPTY_RESOURCES_APK);
+ }
assertTrue(resApkFile.isFile());
-
- final var res = resApkFile.getAbsolutePath();
-
+ final String res = resApkFile.getAbsolutePath();
final var emptyPaths = new String[0];
ResourcesManager.getInstance().initializeApplicationPaths(res, emptyPaths);
@@ -243,102 +228,7 @@
public static void validate(Statement base, Description description,
boolean enableOptionalValidation) {
- validateTestRunner(base, description, enableOptionalValidation);
- validateTestAnnotations(base, description, enableOptionalValidation);
- }
-
- private static void validateTestRunner(Statement base, Description description,
- boolean shouldFail) {
- final var testClass = description.getTestClass();
- final var runWith = testClass.getAnnotation(RunWith.class);
- if (runWith == null) {
- return;
- }
-
- // Due to build dependencies, we can't directly refer to androidx classes here,
- // so just check the class name instead.
- if (runWith.value().getCanonicalName().equals("androidx.test.runner.AndroidJUnit4")) {
- var message = "Test " + testClass.getCanonicalName() + " uses deprecated"
- + " test runner androidx.test.runner.AndroidJUnit4."
- + " Switch to androidx.test.ext.junit.runners.AndroidJUnit4.";
- if (shouldFail) {
- Assert.fail(message);
- } else {
- System.err.println("Warning: " + message);
- }
- }
- }
-
- /**
- * @return if a method has any of annotations.
- */
- private static boolean hasAnyAnnotations(Method m, Class<? extends Annotation>... annotations) {
- for (var anno : annotations) {
- if (m.getAnnotation(anno) != null) {
- return true;
- }
- }
- return false;
- }
-
- private static void validateTestAnnotations(Statement base, Description description,
- boolean enableOptionalValidation) {
- final var testClass = description.getTestClass();
-
- final var message = new StringBuilder();
-
- boolean hasErrors = false;
- for (Method m : collectMethods(testClass)) {
- if (Modifier.isPublic(m.getModifiers()) && m.getName().startsWith("test")) {
- if (!hasAnyAnnotations(m, Test.class, Before.class, After.class,
- BeforeClass.class, AfterClass.class)) {
- message.append("\nMethod " + m.getName() + "() doesn't have @Test");
- hasErrors = true;
- }
- }
- if ("setUp".equals(m.getName())) {
- if (!hasAnyAnnotations(m, Before.class)) {
- message.append("\nMethod " + m.getName() + "() doesn't have @Before");
- hasErrors = true;
- }
- if (!Modifier.isPublic(m.getModifiers())) {
- message.append("\nMethod " + m.getName() + "() must be public");
- hasErrors = true;
- }
- }
- if ("tearDown".equals(m.getName())) {
- if (!hasAnyAnnotations(m, After.class)) {
- message.append("\nMethod " + m.getName() + "() doesn't have @After");
- hasErrors = true;
- }
- if (!Modifier.isPublic(m.getModifiers())) {
- message.append("\nMethod " + m.getName() + "() must be public");
- hasErrors = true;
- }
- }
- }
- assertFalse("Problem(s) detected in class " + testClass.getCanonicalName() + ":"
- + message, hasErrors);
- }
-
- /**
- * Collect all (public or private or any) methods in a class, including inherited methods.
- */
- private static List<Method> collectMethods(Class<?> clazz) {
- var ret = new ArrayList<Method>();
- collectMethods(clazz, ret);
- return ret;
- }
-
- private static void collectMethods(Class<?> clazz, List<Method> result) {
- // Class.getMethods() only return public methods, so we need to use getDeclaredMethods()
- // instead, and recurse.
- for (var m : clazz.getDeclaredMethods()) {
- result.add(m);
- }
- if (clazz.getSuperclass() != null) {
- collectMethods(clazz.getSuperclass(), result);
- }
+ // Nothing to check, for now.
}
/**
diff --git a/ravenwood/runtime-common-src/com/android/ravenwood/common/RavenwoodCommonUtils.java b/ravenwood/runtime-common-src/com/android/ravenwood/common/RavenwoodCommonUtils.java
index 1298023..9a11a8a 100644
--- a/ravenwood/runtime-common-src/com/android/ravenwood/common/RavenwoodCommonUtils.java
+++ b/ravenwood/runtime-common-src/com/android/ravenwood/common/RavenwoodCommonUtils.java
@@ -42,12 +42,15 @@
private static final boolean IS_ON_RAVENWOOD = RavenwoodDivergence.isOnRavenwood();
- private static final String RAVEWOOD_RUNTIME_PATH = getRavenwoodRuntimePathInternal();
+ private static final String RAVENWOOD_RUNTIME_PATH = getRavenwoodRuntimePathInternal();
public static final String RAVENWOOD_SYSPROP = "ro.is_on_ravenwood";
public static final String RAVENWOOD_RESOURCE_APK = "ravenwood-res-apks/ravenwood-res.apk";
+ public static final String RAVENWOOD_EMPTY_RESOURCES_APK =
+ RAVENWOOD_RUNTIME_PATH + "ravenwood-data/ravenwood-empty-res.apk";
+
// @GuardedBy("sLock")
private static boolean sIntegrityChecked = false;
@@ -178,7 +181,7 @@
*/
public static String getRavenwoodRuntimePath() {
ensureOnRavenwood();
- return RAVEWOOD_RUNTIME_PATH;
+ return RAVENWOOD_RUNTIME_PATH;
}
private static String getRavenwoodRuntimePathInternal() {
diff --git a/ravenwood/services-test/test/com/android/ravenwoodtest/servicestest/RavenwoodServicesTest.java b/ravenwood/services-test/test/com/android/ravenwoodtest/servicestest/RavenwoodServicesTest.java
index 044239f..b3d3963 100644
--- a/ravenwood/services-test/test/com/android/ravenwoodtest/servicestest/RavenwoodServicesTest.java
+++ b/ravenwood/services-test/test/com/android/ravenwoodtest/servicestest/RavenwoodServicesTest.java
@@ -16,6 +16,7 @@
package com.android.ravenwoodtest.servicestest;
+import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
@@ -62,7 +63,9 @@
final SerialManager service = (SerialManager)
mRavenwood.getContext().getSystemService(Context.SERIAL_SERVICE);
final String[] ports = service.getSerialPorts();
- assertEquals(0, ports.length);
+ final String[] refPorts = mRavenwood.getContext().getResources().getStringArray(
+ com.android.internal.R.array.config_serialPorts);
+ assertArrayEquals(refPorts, ports);
}
@Test
diff --git a/services/core/java/com/android/server/SerialService.java b/services/core/java/com/android/server/SerialService.java
index 82c2038..dbf144f 100644
--- a/services/core/java/com/android/server/SerialService.java
+++ b/services/core/java/com/android/server/SerialService.java
@@ -56,16 +56,11 @@
}
}
- @android.ravenwood.annotation.RavenwoodReplace
private static String[] getSerialPorts(Context context) {
return context.getResources().getStringArray(
com.android.internal.R.array.config_serialPorts);
}
- private static String[] getSerialPorts$ravenwood(Context context) {
- return new String[0];
- }
-
public static class Lifecycle extends SystemService {
private SerialService mService;
diff --git a/services/core/java/com/android/server/app/GameManagerService.java b/services/core/java/com/android/server/app/GameManagerService.java
index e4c65bd2..8c5152f 100644
--- a/services/core/java/com/android/server/app/GameManagerService.java
+++ b/services/core/java/com/android/server/app/GameManagerService.java
@@ -2307,7 +2307,7 @@
return;
}
- final int userId = mContext.getUserId();
+ final int userId = ActivityManager.getCurrentUser();
final boolean isNotGame = Arrays.stream(packages).noneMatch(
p -> isPackageGame(p, userId));
synchronized (mUidObserverLock) {
diff --git a/services/core/java/com/android/server/notification/GroupHelper.java b/services/core/java/com/android/server/notification/GroupHelper.java
index 008746c..4fa7112 100644
--- a/services/core/java/com/android/server/notification/GroupHelper.java
+++ b/services/core/java/com/android/server/notification/GroupHelper.java
@@ -122,8 +122,9 @@
getNotificationShadeSections();
private static List<NotificationSectioner> getNotificationShadeSections() {
+ ArrayList<NotificationSectioner> sectionsList = new ArrayList<>();
if (android.service.notification.Flags.notificationClassification()) {
- return List.of(
+ sectionsList.addAll(List.of(
new NotificationSectioner("PromotionsSection", 0, (record) ->
NotificationChannel.PROMOTIONS_ID.equals(record.getChannel().getId())),
new NotificationSectioner("SocialSection", 0, (record) ->
@@ -131,18 +132,36 @@
new NotificationSectioner("NewsSection", 0, (record) ->
NotificationChannel.NEWS_ID.equals(record.getChannel().getId())),
new NotificationSectioner("RecsSection", 0, (record) ->
- NotificationChannel.RECS_ID.equals(record.getChannel().getId())),
- new NotificationSectioner("AlertingSection", 0, (record) ->
- record.getImportance() >= NotificationManager.IMPORTANCE_DEFAULT),
- new NotificationSectioner("SilentSection", 1, (record) ->
- record.getImportance() < NotificationManager.IMPORTANCE_DEFAULT));
- } else {
- return List.of(
- new NotificationSectioner("AlertingSection", 0, (record) ->
- record.getImportance() >= NotificationManager.IMPORTANCE_DEFAULT),
- new NotificationSectioner("SilentSection", 1, (record) ->
- record.getImportance() < NotificationManager.IMPORTANCE_DEFAULT));
+ NotificationChannel.RECS_ID.equals(record.getChannel().getId()))));
}
+
+ if (Flags.notificationForceGroupConversations()) {
+ // add priority people section
+ sectionsList.add(new NotificationSectioner("PeopleSection(priority)", 1, (record) ->
+ record.isConversation() && record.getChannel().isImportantConversation()));
+
+ if (android.app.Flags.sortSectionByTime()) {
+ // add single people (alerting) section
+ sectionsList.add(new NotificationSectioner("PeopleSection", 0,
+ NotificationRecord::isConversation));
+ } else {
+ // add people alerting section
+ sectionsList.add(new NotificationSectioner("PeopleSection(alerting)", 1, (record) ->
+ record.isConversation()
+ && record.getImportance() >= NotificationManager.IMPORTANCE_DEFAULT));
+ // add people silent section
+ sectionsList.add(new NotificationSectioner("PeopleSection(silent)", 1, (record) ->
+ record.isConversation()
+ && record.getImportance() < NotificationManager.IMPORTANCE_DEFAULT));
+ }
+ }
+
+ sectionsList.addAll(List.of(
+ new NotificationSectioner("AlertingSection", 0, (record) ->
+ record.getImportance() >= NotificationManager.IMPORTANCE_DEFAULT),
+ new NotificationSectioner("SilentSection", 1, (record) ->
+ record.getImportance() < NotificationManager.IMPORTANCE_DEFAULT)));
+ return sectionsList;
}
public GroupHelper(Context context, PackageManager packageManager, int autoGroupAtCount,
@@ -830,61 +849,19 @@
}
}
+ // The list of notification operations required after the channel update
final ArrayList<NotificationMoveOp> notificationsToMove = new ArrayList<>();
- final Set<FullyQualifiedGroupKey> oldGroups =
- new HashSet<>(mAggregatedNotifications.keySet());
- for (FullyQualifiedGroupKey oldFullAggKey : oldGroups) {
- // Only check aggregate groups that match the same userId & packageName
- if (pkgName.equals(oldFullAggKey.pkg) && userId == oldFullAggKey.userId) {
- final ArrayMap<String, NotificationAttributes> notificationsInAggGroup =
- mAggregatedNotifications.get(oldFullAggKey);
- if (notificationsInAggGroup == null) {
- continue;
- }
+ // Check any already auto-grouped notifications that may need to be re-grouped
+ // after the channel update
+ notificationsToMove.addAll(
+ getAutogroupedNotificationsMoveOps(userId, pkgName,
+ notificationsToCheck));
- FullyQualifiedGroupKey newFullAggregateGroupKey = null;
- for (String key : notificationsInAggGroup.keySet()) {
- if (notificationsToCheck.get(key) != null) {
- // check if section changes
- NotificationSectioner sectioner = getSection(
- notificationsToCheck.get(key));
- if (sectioner == null) {
- continue;
- }
- newFullAggregateGroupKey = new FullyQualifiedGroupKey(userId, pkgName,
- sectioner);
- if (!oldFullAggKey.equals(newFullAggregateGroupKey)) {
- if (DEBUG) {
- Log.i(TAG, "Change section on channel update: " + key);
- }
- notificationsToMove.add(
- new NotificationMoveOp(notificationsToCheck.get(key),
- oldFullAggKey, newFullAggregateGroupKey));
- }
- }
- }
-
- if (newFullAggregateGroupKey != null) {
- // Add any notifications left ungrouped to the new section
- ArrayMap<String, NotificationAttributes> ungrouped =
- mUngroupedAbuseNotifications.get(newFullAggregateGroupKey);
- if (ungrouped != null) {
- for (NotificationRecord r : notificationList) {
- if (ungrouped.containsKey(r.getKey())) {
- if (DEBUG) {
- Log.i(TAG, "Add previously ungrouped: " + r);
- }
- notificationsToMove.add(
- new NotificationMoveOp(r, null, newFullAggregateGroupKey));
- }
- }
- //Cleanup mUngroupedAbuseNotifications
- mUngroupedAbuseNotifications.remove(newFullAggregateGroupKey);
- }
- }
- }
- }
+ // Check any ungrouped notifications that may need to be auto-grouped
+ // after the channel update
+ notificationsToMove.addAll(
+ getUngroupedNotificationsMoveOps(userId, pkgName, notificationsToCheck));
// Batch move to new section
if (!notificationsToMove.isEmpty()) {
@@ -894,10 +871,103 @@
}
@GuardedBy("mAggregatedNotifications")
+ private List<NotificationMoveOp> getAutogroupedNotificationsMoveOps(int userId, String pkgName,
+ ArrayMap<String, NotificationRecord> notificationsToCheck) {
+ final ArrayList<NotificationMoveOp> notificationsToMove = new ArrayList<>();
+ final Set<FullyQualifiedGroupKey> oldGroups =
+ new HashSet<>(mAggregatedNotifications.keySet());
+ // Move auto-grouped updated notifications from the old groups to the new groups (section)
+ for (FullyQualifiedGroupKey oldFullAggKey : oldGroups) {
+ // Only check aggregate groups that match the same userId & packageName
+ if (pkgName.equals(oldFullAggKey.pkg) && userId == oldFullAggKey.userId) {
+ final ArrayMap<String, NotificationAttributes> notificationsInAggGroup =
+ mAggregatedNotifications.get(oldFullAggKey);
+ if (notificationsInAggGroup == null) {
+ continue;
+ }
+
+ FullyQualifiedGroupKey newFullAggregateGroupKey = null;
+ for (String key : notificationsInAggGroup.keySet()) {
+ if (notificationsToCheck.get(key) != null) {
+ // check if section changes
+ NotificationSectioner sectioner = getSection(notificationsToCheck.get(key));
+ if (sectioner == null) {
+ continue;
+ }
+ newFullAggregateGroupKey = new FullyQualifiedGroupKey(userId, pkgName,
+ sectioner);
+ if (!oldFullAggKey.equals(newFullAggregateGroupKey)) {
+ if (DEBUG) {
+ Log.i(TAG, "Change section on channel update: " + key);
+ }
+ notificationsToMove.add(
+ new NotificationMoveOp(notificationsToCheck.get(key),
+ oldFullAggKey, newFullAggregateGroupKey));
+ notificationsToCheck.remove(key);
+ }
+ }
+ }
+ }
+ }
+ return notificationsToMove;
+ }
+
+ @GuardedBy("mAggregatedNotifications")
+ private List<NotificationMoveOp> getUngroupedNotificationsMoveOps(int userId, String pkgName,
+ final ArrayMap<String, NotificationRecord> notificationsToCheck) {
+ final ArrayList<NotificationMoveOp> notificationsToMove = new ArrayList<>();
+ // Move any remaining ungrouped updated notifications from the old ungrouped list
+ // to the new ungrouped section list, if necessary
+ if (!notificationsToCheck.isEmpty()) {
+ final Set<FullyQualifiedGroupKey> oldUngroupedSectionKeys =
+ new HashSet<>(mUngroupedAbuseNotifications.keySet());
+ for (FullyQualifiedGroupKey oldFullAggKey : oldUngroupedSectionKeys) {
+ // Only check aggregate groups that match the same userId & packageName
+ if (pkgName.equals(oldFullAggKey.pkg) && userId == oldFullAggKey.userId) {
+ final ArrayMap<String, NotificationAttributes> ungroupedOld =
+ mUngroupedAbuseNotifications.get(oldFullAggKey);
+ if (ungroupedOld == null) {
+ continue;
+ }
+
+ FullyQualifiedGroupKey newFullAggregateGroupKey = null;
+ final Set<String> ungroupedKeys = new HashSet<>(ungroupedOld.keySet());
+ for (String key : ungroupedKeys) {
+ NotificationRecord record = notificationsToCheck.get(key);
+ if (record != null) {
+ // check if section changes
+ NotificationSectioner sectioner = getSection(record);
+ if (sectioner == null) {
+ continue;
+ }
+ newFullAggregateGroupKey = new FullyQualifiedGroupKey(userId, pkgName,
+ sectioner);
+ if (!oldFullAggKey.equals(newFullAggregateGroupKey)) {
+ if (DEBUG) {
+ Log.i(TAG, "Change ungrouped section: " + key);
+ }
+ notificationsToMove.add(
+ new NotificationMoveOp(record, oldFullAggKey,
+ newFullAggregateGroupKey));
+ notificationsToCheck.remove(key);
+ //Remove from previous ungrouped list
+ ungroupedOld.remove(key);
+ }
+ }
+ }
+ mUngroupedAbuseNotifications.put(oldFullAggKey, ungroupedOld);
+ }
+ }
+ }
+ return notificationsToMove;
+ }
+
+ @GuardedBy("mAggregatedNotifications")
private void moveNotificationsToNewSection(final int userId, final String pkgName,
final List<NotificationMoveOp> notificationsToMove) {
record GroupUpdateOp(FullyQualifiedGroupKey groupKey, NotificationRecord record,
boolean hasSummary) { }
+ // Bundled operations to apply to groups affected by the channel update
ArrayMap<FullyQualifiedGroupKey, GroupUpdateOp> groupsToUpdate = new ArrayMap<>();
for (NotificationMoveOp moveOp: notificationsToMove) {
@@ -923,35 +993,36 @@
// Only add once, for triggering notification
if (!groupsToUpdate.containsKey(oldFullAggregateGroupKey)) {
groupsToUpdate.put(oldFullAggregateGroupKey,
- new GroupUpdateOp(oldFullAggregateGroupKey, record, true));
+ new GroupUpdateOp(oldFullAggregateGroupKey, record, true));
}
}
- // Add/update aggregate summary for new group
+ // Add moved notifications to the ungrouped list for new group and do grouping
+ // after all notifications have been handled
if (newFullAggregateGroupKey != null) {
final ArrayMap<String, NotificationAttributes> newAggregatedNotificationsAttrs =
mAggregatedNotifications.getOrDefault(newFullAggregateGroupKey,
new ArrayMap<>());
- boolean newGroupExists = !newAggregatedNotificationsAttrs.isEmpty();
- newAggregatedNotificationsAttrs.put(record.getKey(),
- new NotificationAttributes(record.getFlags(),
- record.getNotification().getSmallIcon(),
- record.getNotification().color,
- record.getNotification().visibility,
- record.getNotification().getGroupAlertBehavior(),
- record.getChannel().getId()));
- mAggregatedNotifications.put(newFullAggregateGroupKey,
- newAggregatedNotificationsAttrs);
+ boolean hasSummary = !newAggregatedNotificationsAttrs.isEmpty();
+ ArrayMap<String, NotificationAttributes> ungrouped =
+ mUngroupedAbuseNotifications.getOrDefault(newFullAggregateGroupKey,
+ new ArrayMap<>());
+ ungrouped.put(record.getKey(), new NotificationAttributes(
+ record.getFlags(),
+ record.getNotification().getSmallIcon(),
+ record.getNotification().color,
+ record.getNotification().visibility,
+ record.getNotification().getGroupAlertBehavior(),
+ record.getChannel().getId()));
+ mUngroupedAbuseNotifications.put(newFullAggregateGroupKey, ungrouped);
+
+ record.setOverrideGroupKey(null);
// Only add once, for triggering notification
if (!groupsToUpdate.containsKey(newFullAggregateGroupKey)) {
groupsToUpdate.put(newFullAggregateGroupKey,
- new GroupUpdateOp(newFullAggregateGroupKey, record, newGroupExists));
+ new GroupUpdateOp(newFullAggregateGroupKey, record, hasSummary));
}
-
- // Add notification to new group. do not request resort
- record.setOverrideGroupKey(null);
- mCallback.addAutoGroup(record.getKey(), newFullAggregateGroupKey.toString(), false);
}
}
@@ -959,18 +1030,26 @@
for (FullyQualifiedGroupKey groupKey : groupsToUpdate.keySet()) {
final ArrayMap<String, NotificationAttributes> aggregatedNotificationsAttrs =
mAggregatedNotifications.getOrDefault(groupKey, new ArrayMap<>());
- if (aggregatedNotificationsAttrs.isEmpty()) {
- mCallback.removeAutoGroupSummary(userId, pkgName, groupKey.toString());
- mAggregatedNotifications.remove(groupKey);
- } else {
- NotificationRecord triggeringNotification = groupsToUpdate.get(groupKey).record;
- boolean hasSummary = groupsToUpdate.get(groupKey).hasSummary;
+ final ArrayMap<String, NotificationAttributes> ungrouped =
+ mUngroupedAbuseNotifications.getOrDefault(groupKey, new ArrayMap<>());
+
+ NotificationRecord triggeringNotification = groupsToUpdate.get(groupKey).record;
+ boolean hasSummary = groupsToUpdate.get(groupKey).hasSummary;
+ //Group needs to be created/updated
+ if (ungrouped.size() >= mAutoGroupAtCount
+ || (hasSummary && !aggregatedNotificationsAttrs.isEmpty())) {
NotificationSectioner sectioner = getSection(triggeringNotification);
if (sectioner == null) {
continue;
}
- updateAggregateAppGroup(groupKey, triggeringNotification.getKey(), hasSummary,
- sectioner.mSummaryId);
+ aggregateUngroupedNotifications(groupKey, triggeringNotification.getKey(),
+ ungrouped, hasSummary, sectioner.mSummaryId);
+ } else {
+ // Remove empty groups
+ if (aggregatedNotificationsAttrs.isEmpty() && hasSummary) {
+ mCallback.removeAutoGroupSummary(userId, pkgName, groupKey.toString());
+ mAggregatedNotifications.remove(groupKey);
+ }
}
}
}
@@ -1327,8 +1406,10 @@
}
private boolean isNotificationGroupable(final NotificationRecord record) {
- if (record.isConversation()) {
- return false;
+ if (!Flags.notificationForceGroupConversations()) {
+ if (record.isConversation()) {
+ return false;
+ }
}
Notification notification = record.getSbn().getNotification();
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 06d1285..6ff0e04 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -765,7 +765,9 @@
try {
ApplicationInfo applicationInfo = mPm.getApplicationInfo(pkg, 0);
rule.name = applicationInfo.loadLabel(mPm).toString();
- rule.iconResName = drawableResIdToResName(pkg, applicationInfo.icon);
+ if (!Flags.modesUi()) {
+ rule.iconResName = drawableResIdToResName(pkg, applicationInfo.icon);
+ }
} catch (PackageManager.NameNotFoundException e) {
// Should not happen, since it's the app calling us (?)
Log.w(TAG, "Package not found for creating implicit zen rule");
@@ -1750,6 +1752,15 @@
manualRulePolicy.overwrittenWith(automaticRule.zenPolicy);
}
}
+
+ if (Flags.modesApi() && Flags.modesUi()
+ && config.version < ZenModeConfig.XML_VERSION_MODES_UI) {
+ // Clear icons from implicit rules. App icons are not suitable for some
+ // surfaces, so juse use a default (the user can select a different one).
+ if (ZenModeConfig.isImplicitRuleId(automaticRule.id)) {
+ automaticRule.iconResName = null;
+ }
+ }
}
}
diff --git a/services/core/java/com/android/server/notification/flags.aconfig b/services/core/java/com/android/server/notification/flags.aconfig
index 7265cff..aac2c40 100644
--- a/services/core/java/com/android/server/notification/flags.aconfig
+++ b/services/core/java/com/android/server/notification/flags.aconfig
@@ -149,3 +149,10 @@
description: "This flag enables forced auto-grouping singleton groups"
bug: "336488844"
}
+
+flag {
+ name: "notification_force_group_conversations"
+ namespace: "systemui"
+ description: "This flag enables forced auto-grouping conversations"
+ bug: "336488844"
+}
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 2294d65..47d4740 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -233,7 +233,6 @@
import static com.android.server.wm.StartingData.AFTER_TRANSACTION_REMOVE_DIRECTLY;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_PREDICT_BACK;
-import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_RECENTS;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_WINDOW_ANIMATION;
import static com.android.server.wm.TaskFragment.TASK_FRAGMENT_VISIBILITY_VISIBLE;
import static com.android.server.wm.TaskPersister.DEBUG;
@@ -5542,10 +5541,7 @@
return false;
}
if (!mDisplayContent.mAppTransition.isTransitionSet()) {
- // Defer committing visibility for non-home app which is animating by recents.
- if (isActivityTypeHome() || !isAnimating(PARENTS, ANIMATION_TYPE_RECENTS)) {
- return false;
- }
+ return false;
}
if (mWaitForEnteringPinnedMode && mVisible == visible) {
// If the visibility is not changed during enter PIP, we don't want to include it in
@@ -5699,8 +5695,7 @@
private void postApplyAnimation(boolean visible, boolean fromTransition) {
final boolean usingShellTransitions = mTransitionController.isShellTransitionsEnabled();
final boolean delayed = !usingShellTransitions && isAnimating(PARENTS | CHILDREN,
- ANIMATION_TYPE_APP_TRANSITION | ANIMATION_TYPE_WINDOW_ANIMATION
- | ANIMATION_TYPE_RECENTS);
+ ANIMATION_TYPE_APP_TRANSITION | ANIMATION_TYPE_WINDOW_ANIMATION);
if (!delayed && !usingShellTransitions) {
// We aren't delayed anything, but exiting windows rely on the animation finished
// callback being called in case the ActivityRecord was pretending to be delayed,
@@ -5722,7 +5717,7 @@
// animation and aren't in RESUMED state. Otherwise, we'll update client visibility in
// onAnimationFinished or activityStopped.
if (visible || (mState != RESUMED && (usingShellTransitions || !isAnimating(
- PARENTS, ANIMATION_TYPE_APP_TRANSITION | ANIMATION_TYPE_RECENTS)))) {
+ PARENTS, ANIMATION_TYPE_APP_TRANSITION)))) {
setClientVisible(visible);
}
@@ -7700,7 +7695,7 @@
// Ensure that the activity content is hidden when the decor surface is boosted to
// prevent UI redressing attack.
&& !isDecorSurfaceBoosted)
- || isAnimating(PARENTS, ANIMATION_TYPE_APP_TRANSITION | ANIMATION_TYPE_RECENTS
+ || isAnimating(PARENTS, ANIMATION_TYPE_APP_TRANSITION
| ANIMATION_TYPE_PREDICT_BACK);
if (mSurfaceControl != null) {
diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java
index 06bdc04..197bd5a 100644
--- a/services/core/java/com/android/server/wm/AppTransitionController.java
+++ b/services/core/java/com/android/server/wm/AppTransitionController.java
@@ -68,7 +68,6 @@
import static com.android.server.wm.NonAppWindowAnimationAdapter.shouldAttachNavBarToApp;
import static com.android.server.wm.NonAppWindowAnimationAdapter.shouldStartNonAppWindowAnimationsForKeyguardExit;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION;
-import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_RECENTS;
import static com.android.server.wm.WallpaperAnimationAdapter.shouldStartWallpaperAnimation;
import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
@@ -173,41 +172,6 @@
|| !transitionGoodToGoForTaskFragments()) {
return;
}
- final boolean isRecentsInOpening = mDisplayContent.mOpeningApps.stream().anyMatch(
- ConfigurationContainer::isActivityTypeRecents);
- // In order to avoid visual clutter caused by a conflict between app transition
- // animation and recents animation, app transition is delayed until recents finishes.
- // One exceptional case. When 3P launcher is used and a user taps a task screenshot in
- // task switcher (isRecentsInOpening=true), app transition must start even though
- // recents is running. Otherwise app transition is blocked until timeout (b/232984498).
- // When 1P launcher is used, this animation is controlled by the launcher outside of
- // the app transition, so delaying app transition doesn't cause visible delay. After
- // recents finishes, app transition is handled just to commit visibility on apps.
- if (!isRecentsInOpening) {
- final ArraySet<WindowContainer> participants = new ArraySet<>();
- participants.addAll(mDisplayContent.mOpeningApps);
- participants.addAll(mDisplayContent.mChangingContainers);
- boolean deferForRecents = false;
- for (int i = 0; i < participants.size(); i++) {
- WindowContainer wc = participants.valueAt(i);
- final ActivityRecord activity = getAppFromContainer(wc);
- if (activity == null) {
- continue;
- }
- // Don't defer recents animation if one of activity isn't running for it, that one
- // might be started from quickstep.
- if (!activity.isAnimating(PARENTS, ANIMATION_TYPE_RECENTS)) {
- deferForRecents = false;
- break;
- }
- deferForRecents = true;
- }
- if (deferForRecents) {
- ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
- "Delaying app transition for recents animation to finish");
- return;
- }
- }
Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "AppTransitionReady");
diff --git a/services/core/java/com/android/server/wm/SurfaceAnimator.java b/services/core/java/com/android/server/wm/SurfaceAnimator.java
index 9cfd396..57f9be0 100644
--- a/services/core/java/com/android/server/wm/SurfaceAnimator.java
+++ b/services/core/java/com/android/server/wm/SurfaceAnimator.java
@@ -32,8 +32,8 @@
import android.view.SurfaceControl.Transaction;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.protolog.common.LogLevel;
import com.android.internal.protolog.ProtoLog;
+import com.android.internal.protolog.common.LogLevel;
import java.io.PrintWriter;
import java.io.StringWriter;
@@ -585,7 +585,6 @@
ANIMATION_TYPE_APP_TRANSITION,
ANIMATION_TYPE_SCREEN_ROTATION,
ANIMATION_TYPE_DIMMER,
- ANIMATION_TYPE_RECENTS,
ANIMATION_TYPE_WINDOW_ANIMATION,
ANIMATION_TYPE_INSETS_CONTROL,
ANIMATION_TYPE_TOKEN_TRANSFORM,
@@ -604,7 +603,6 @@
case ANIMATION_TYPE_APP_TRANSITION: return "app_transition";
case ANIMATION_TYPE_SCREEN_ROTATION: return "screen_rotation";
case ANIMATION_TYPE_DIMMER: return "dimmer";
- case ANIMATION_TYPE_RECENTS: return "recents_animation";
case ANIMATION_TYPE_WINDOW_ANIMATION: return "window_animation";
case ANIMATION_TYPE_INSETS_CONTROL: return "insets_animation";
case ANIMATION_TYPE_TOKEN_TRANSFORM: return "token_transform";
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index f000223..21be0fc 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -94,7 +94,6 @@
import static com.android.server.wm.LockTaskController.LOCK_TASK_AUTH_LAUNCHABLE;
import static com.android.server.wm.LockTaskController.LOCK_TASK_AUTH_LAUNCHABLE_PRIV;
import static com.android.server.wm.LockTaskController.LOCK_TASK_AUTH_PINNABLE;
-import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_RECENTS;
import static com.android.server.wm.TaskProto.AFFINITY;
import static com.android.server.wm.TaskProto.BOUNDS;
import static com.android.server.wm.TaskProto.CREATED_BY_ORGANIZER;
@@ -2966,8 +2965,7 @@
/** Checking if self or its child tasks are animated by recents animation. */
boolean isAnimatingByRecents() {
- return isAnimating(CHILDREN, ANIMATION_TYPE_RECENTS)
- || mTransitionController.isTransientHide(this);
+ return mTransitionController.isTransientHide(this);
}
WindowState getTopVisibleAppMainWindow() {
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index 03342d3..13334a5 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -19,7 +19,6 @@
import static com.android.internal.protolog.ProtoLogGroup.WM_SHOW_TRANSACTIONS;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_ALL;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION;
-import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_RECENTS;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_SCREEN_ROTATION;
import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN;
import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
@@ -218,8 +217,8 @@
private void updateRunningExpensiveAnimationsLegacy() {
final boolean runningExpensiveAnimations =
mService.mRoot.isAnimating(TRANSITION | CHILDREN /* flags */,
- ANIMATION_TYPE_APP_TRANSITION | ANIMATION_TYPE_SCREEN_ROTATION
- | ANIMATION_TYPE_RECENTS /* typesToCheck */);
+ ANIMATION_TYPE_APP_TRANSITION
+ | ANIMATION_TYPE_SCREEN_ROTATION /* typesToCheck */);
if (runningExpensiveAnimations && !mRunningExpensiveAnimations) {
mService.mSnapshotController.setPause(true);
mTransaction.setEarlyWakeupStart();
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index a980b77..6995027 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -51,7 +51,6 @@
import static com.android.server.wm.IdentifierProto.USER_ID;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_ALL;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION;
-import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_RECENTS;
import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN;
import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS;
import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
@@ -1242,8 +1241,7 @@
*/
boolean inTransitionSelfOrParent() {
if (!mTransitionController.isShellTransitionsEnabled()) {
- return isAnimating(PARENTS | TRANSITION,
- ANIMATION_TYPE_APP_TRANSITION | ANIMATION_TYPE_RECENTS);
+ return isAnimating(PARENTS | TRANSITION, ANIMATION_TYPE_APP_TRANSITION);
}
return inTransition();
}
diff --git a/services/core/java/com/android/server/wm/WindowContainerThumbnail.java b/services/core/java/com/android/server/wm/WindowContainerThumbnail.java
index ad88062..80f3c44 100644
--- a/services/core/java/com/android/server/wm/WindowContainerThumbnail.java
+++ b/services/core/java/com/android/server/wm/WindowContainerThumbnail.java
@@ -20,7 +20,7 @@
import static android.view.SurfaceControl.METADATA_WINDOW_TYPE;
import static com.android.internal.protolog.ProtoLogGroup.WM_SHOW_TRANSACTIONS;
-import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_RECENTS;
+import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION;
import static com.android.server.wm.WindowContainerThumbnailProto.HEIGHT;
import static com.android.server.wm.WindowContainerThumbnailProto.SURFACE_ANIMATOR;
import static com.android.server.wm.WindowContainerThumbnailProto.WIDTH;
@@ -118,7 +118,7 @@
mWindowContainer.getDisplayContent().mAppTransition.canSkipFirstFrame(),
mWindowContainer.getDisplayContent().getWindowCornerRadius()),
mWindowContainer.mWmService.mSurfaceAnimationRunner), false /* hidden */,
- ANIMATION_TYPE_RECENTS);
+ ANIMATION_TYPE_APP_TRANSITION);
}
private void onAnimationFinished(@AnimationType int type, AnimationAdapter anim) {
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index eeda2a7..9b55ed2 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -129,7 +129,6 @@
import static com.android.server.wm.SensitiveContentPackages.PackageInfo;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_ALL;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION;
-import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_RECENTS;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_WINDOW_ANIMATION;
import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN;
import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS;
@@ -2725,8 +2724,7 @@
win.mTransitionController.mAnimatingExitWindows.add(win);
reason = "inTransition";
}
- } else if (win.isAnimating(PARENTS | TRANSITION,
- ANIMATION_TYPE_APP_TRANSITION | ANIMATION_TYPE_RECENTS)) {
+ } else if (win.isAnimating(PARENTS | TRANSITION, ANIMATION_TYPE_APP_TRANSITION)) {
// Already animating as part of a legacy app-transition.
reason = "inLegacyTransition";
}
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 81bce18..c8e4b0a 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -128,7 +128,6 @@
import static com.android.server.wm.MoveAnimationSpecProto.TO;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_ALL;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION;
-import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_RECENTS;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_STARTING_REVEAL;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_WINDOW_ANIMATION;
import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS;
@@ -581,7 +580,7 @@
* is guaranteed to be cleared.
*/
static final int EXIT_ANIMATING_TYPES = ANIMATION_TYPE_APP_TRANSITION
- | ANIMATION_TYPE_WINDOW_ANIMATION | ANIMATION_TYPE_RECENTS;
+ | ANIMATION_TYPE_WINDOW_ANIMATION;
/** Currently running an exit animation? */
boolean mAnimatingExit;
diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/InputMethodServiceTest.java b/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/InputMethodServiceTest.java
index 0787058..2c78504 100644
--- a/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/InputMethodServiceTest.java
+++ b/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/InputMethodServiceTest.java
@@ -33,11 +33,15 @@
import android.content.res.Configuration;
import android.graphics.Insets;
import android.os.RemoteException;
+import android.platform.test.annotations.RequiresFlagsDisabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.provider.Settings;
import android.util.Log;
import android.view.WindowManagerGlobal;
import android.view.WindowManagerPolicyConstants;
import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.Flags;
import android.view.inputmethod.InputMethodManager;
import androidx.annotation.NonNull;
@@ -56,6 +60,7 @@
import org.junit.After;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -89,6 +94,9 @@
private String mInputMethodId;
private boolean mShowImeWithHardKeyboardEnabled;
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
@Before
public void setUp() throws Exception {
mInstrumentation = InstrumentationRegistry.getInstrumentation();
@@ -155,7 +163,13 @@
() -> assertThat(mUiDevice.pressHome()).isTrue(),
true /* expected */,
false /* inputViewStarted */);
- assertThat(mInputMethodService.isInputViewShown()).isFalse();
+ if (Flags.refactorInsetsController()) {
+ // The IME visibility is only sent at the end of the animation. Therefore, we have to
+ // wait until the visibility was sent to the server and the IME window hidden.
+ eventually(() -> assertThat(mInputMethodService.isInputViewShown()).isFalse());
+ } else {
+ assertThat(mInputMethodService.isInputViewShown()).isFalse();
+ }
}
/**
@@ -182,8 +196,13 @@
/**
* This checks the result of calling IMS#requestShowSelf and IMS#requestHideSelf.
+ *
+ * With the refactor in b/298172246, all calls to IMMS#{show,hide}MySoftInputLocked
+ * will be just apply the requested visibility (by using the callback). Therefore, we will
+ * lose flags like HIDE_IMPLICIT_ONLY.
*/
@Test
+ @RequiresFlagsDisabled(Flags.FLAG_REFACTOR_INSETS_CONTROLLER)
public void testShowHideSelf() throws Exception {
setShowImeWithHardKeyboard(true /* enabled */);
@@ -375,8 +394,13 @@
/**
* This checks that an implicit show request when the IME is not previously shown,
* and it should be shown in fullscreen mode, results in the IME not being shown.
+ *
+ * With the refactor in b/298172246, all calls from InputMethodManager#{show,hide}SoftInput
+ * will be redirected to InsetsController#{show,hide}. Therefore, we will lose flags like
+ * SHOW_IMPLICIT.
*/
@Test
+ @RequiresFlagsDisabled(Flags.FLAG_REFACTOR_INSETS_CONTROLLER)
public void testShowSoftInputImplicitly_fullScreenMode() throws Exception {
setShowImeWithHardKeyboard(true /* enabled */);
@@ -425,8 +449,13 @@
/**
* This checks that an implicit show request when a hard keyboard is connected,
* results in the IME not being shown.
+ *
+ * With the refactor in b/298172246, all calls from InputMethodManager#{show,hide}SoftInput
+ * will be redirected to InsetsController#{show,hide}. Therefore, we will lose flags like
+ * SHOW_IMPLICIT.
*/
@Test
+ @RequiresFlagsDisabled(Flags.FLAG_REFACTOR_INSETS_CONTROLLER)
public void testShowSoftInputImplicitly_withHardKeyboard() throws Exception {
setShowImeWithHardKeyboard(false /* enabled */);
@@ -484,8 +513,13 @@
* This checks that an implicit show request followed by connecting a hard keyboard
* and a configuration change, does not trigger IMS#onFinishInputView,
* but results in the IME being hidden.
+ *
+ * With the refactor in b/298172246, all calls from InputMethodManager#{show,hide}SoftInput
+ * will be redirected to InsetsController#{show,hide}. Therefore, we will lose flags like
+ * SHOW_IMPLICIT.
*/
@Test
+ @RequiresFlagsDisabled(Flags.FLAG_REFACTOR_INSETS_CONTROLLER)
public void testShowSoftInputImplicitly_thenConfigurationChanged() throws Exception {
setShowImeWithHardKeyboard(false /* enabled */);
@@ -567,8 +601,13 @@
* This checks that a forced show request directly followed by an explicit show request,
* and then a hide not always request, still results in the IME being shown
* (i.e. the explicit show request retains the forced state).
+ *
+ * With the refactor in b/298172246, all calls from InputMethodManager#{show,hide}SoftInput
+ * will be redirected to InsetsController#{show,hide}. Therefore, we will lose flags like
+ * HIDE_NOT_ALWAYS.
*/
@Test
+ @RequiresFlagsDisabled(Flags.FLAG_REFACTOR_INSETS_CONTROLLER)
public void testShowSoftInputForced_testShowSoftInputExplicitly_thenHideSoftInputNotAlways()
throws Exception {
setShowImeWithHardKeyboard(true /* enabled */);
@@ -734,7 +773,13 @@
backButtonUiObject.click();
mInstrumentation.waitForIdleSync();
- assertThat(mInputMethodService.isInputViewShown()).isFalse();
+ if (Flags.refactorInsetsController()) {
+ // The IME visibility is only sent at the end of the animation. Therefore, we have to
+ // wait until the visibility was sent to the server and the IME window hidden.
+ eventually(() -> assertThat(mInputMethodService.isInputViewShown()).isFalse());
+ } else {
+ assertThat(mInputMethodService.isInputViewShown()).isFalse();
+ }
}
/**
@@ -766,7 +811,13 @@
backButtonUiObject.longClick();
mInstrumentation.waitForIdleSync();
- assertThat(mInputMethodService.isInputViewShown()).isFalse();
+ if (Flags.refactorInsetsController()) {
+ // The IME visibility is only sent at the end of the animation. Therefore, we have to
+ // wait until the visibility was sent to the server and the IME window hidden.
+ eventually(() -> assertThat(mInputMethodService.isInputViewShown()).isFalse());
+ } else {
+ assertThat(mInputMethodService.isInputViewShown()).isFalse();
+ }
}
/**
@@ -848,7 +899,13 @@
assertWithMessage("Input Method Switcher Menu is shown")
.that(isInputMethodPickerShown(imm))
.isTrue();
- assertThat(mInputMethodService.isInputViewShown()).isTrue();
+ if (Flags.refactorInsetsController()) {
+ // The IME visibility is only sent at the end of the animation. Therefore, we have to
+ // wait until the visibility was sent to the server and the IME window hidden.
+ eventually(() -> assertThat(mInputMethodService.isInputViewShown()).isFalse());
+ } else {
+ assertThat(mInputMethodService.isInputViewShown()).isTrue();
+ }
// Hide the Picker menu before finishing.
mUiDevice.pressBack();
diff --git a/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java b/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java
index 0703db2..18811de 100644
--- a/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java
@@ -223,6 +223,11 @@
mShutDownActionReceiver = receiver;
return null;
}
+
+ @Override
+ public int getUserId() {
+ return 0;
+ }
}
@Before
@@ -237,7 +242,7 @@
mPackageCategories = new HashMap<>();
mPackageUids = new HashMap<>();
mPackageName = mMockContext.getPackageName();
- mockAppCategory(mPackageName, DEFAULT_PACKAGE_UID, ApplicationInfo.CATEGORY_GAME);
+ mockAppCategory(mPackageName, DEFAULT_PACKAGE_UID, ApplicationInfo.CATEGORY_GAME, -1);
LocalServices.addService(PowerManagerInternal.class, mMockPowerManager);
mSetFlagsRule.enableFlags(Flags.FLAG_GAME_DEFAULT_FRAME_RATE);
@@ -245,7 +250,12 @@
}
private void mockAppCategory(String packageName, int packageUid,
- @ApplicationInfo.Category int category)
+ @ApplicationInfo.Category int category) throws Exception {
+ mockAppCategory(packageName, packageUid, category, -1 /*userId*/);
+ }
+
+ private void mockAppCategory(String packageName, int packageUid,
+ @ApplicationInfo.Category int category, int userId)
throws Exception {
reset(mMockPackageManager);
mPackageCategories.put(packageName, category);
@@ -259,8 +269,15 @@
ApplicationInfo applicationInfo = new ApplicationInfo();
applicationInfo.packageName = packageName;
applicationInfo.category = category;
- when(mMockPackageManager.getApplicationInfoAsUser(eq(packageName), anyInt(), anyInt()))
- .thenReturn(applicationInfo);
+ if (userId == -1) {
+ when(mMockPackageManager.getApplicationInfoAsUser(eq(packageName), anyInt(),
+ anyInt()))
+ .thenReturn(applicationInfo);
+ } else {
+ when(mMockPackageManager.getApplicationInfoAsUser(eq(packageName), anyInt(),
+ eq(userId)))
+ .thenReturn(applicationInfo);
+ }
final PackageInfo pi = new PackageInfo();
pi.packageName = packageName;
@@ -2331,10 +2348,12 @@
@Test
public void testGamePowerMode_twoGames() throws Exception {
- GameManagerService gameManagerService = createServiceAndStartUser(USER_ID_1);
+ GameManagerService gameManagerService = new GameManagerService(mMockContext,
+ mTestLooper.getLooper());
String someGamePkg = "some.game";
int somePackageId = DEFAULT_PACKAGE_UID + 1;
- mockAppCategory(someGamePkg, somePackageId, ApplicationInfo.CATEGORY_GAME);
+ mockAppCategory(someGamePkg, somePackageId, ApplicationInfo.CATEGORY_GAME,
+ ActivityManager.getCurrentUser());
HashMap<Integer, Boolean> powerState = new HashMap<>();
doAnswer(inv -> powerState.put(inv.getArgument(0), inv.getArgument(1)))
.when(mMockPowerManager).setPowerMode(anyInt(), anyBoolean());
@@ -2354,10 +2373,12 @@
@Test
public void testGamePowerMode_twoGamesOverlap() throws Exception {
- GameManagerService gameManagerService = createServiceAndStartUser(USER_ID_1);
+ GameManagerService gameManagerService = new GameManagerService(mMockContext,
+ mTestLooper.getLooper());
String someGamePkg = "some.game";
int somePackageId = DEFAULT_PACKAGE_UID + 1;
- mockAppCategory(someGamePkg, somePackageId, ApplicationInfo.CATEGORY_GAME);
+ mockAppCategory(someGamePkg, somePackageId, ApplicationInfo.CATEGORY_GAME,
+ ActivityManager.getCurrentUser());
gameManagerService.mUidObserver.onUidStateChanged(
DEFAULT_PACKAGE_UID, ActivityManager.PROCESS_STATE_TOP, 0, 0);
gameManagerService.mUidObserver.onUidStateChanged(
@@ -2372,7 +2393,8 @@
@Test
public void testGamePowerMode_noPackage() throws Exception {
- GameManagerService gameManagerService = createServiceAndStartUser(USER_ID_1);
+ GameManagerService gameManagerService = new GameManagerService(mMockContext,
+ mTestLooper.getLooper());
String[] packages = {};
when(mMockPackageManager.getPackagesForUid(DEFAULT_PACKAGE_UID)).thenReturn(packages);
gameManagerService.mUidObserver.onUidStateChanged(
@@ -2383,23 +2405,24 @@
@Test
public void testGamePowerMode_gameAndNotGameApps_flagOn() throws Exception {
mSetFlagsRule.enableFlags(Flags.FLAG_DISABLE_GAME_MODE_WHEN_APP_TOP);
- GameManagerService gameManagerService = createServiceAndStartUser(USER_ID_1);
-
+ GameManagerService gameManagerService = new GameManagerService(mMockContext,
+ mTestLooper.getLooper());
+ int userId = ActivityManager.getCurrentUser();
String nonGamePkg1 = "not.game1";
int nonGameUid1 = DEFAULT_PACKAGE_UID + 1;
- mockAppCategory(nonGamePkg1, nonGameUid1, ApplicationInfo.CATEGORY_IMAGE);
+ mockAppCategory(nonGamePkg1, nonGameUid1, ApplicationInfo.CATEGORY_IMAGE, userId);
String nonGamePkg2 = "not.game2";
int nonGameUid2 = DEFAULT_PACKAGE_UID + 2;
- mockAppCategory(nonGamePkg2, nonGameUid2, ApplicationInfo.CATEGORY_IMAGE);
+ mockAppCategory(nonGamePkg2, nonGameUid2, ApplicationInfo.CATEGORY_IMAGE, userId);
String gamePkg1 = "game1";
int gameUid1 = DEFAULT_PACKAGE_UID + 3;
- mockAppCategory(gamePkg1, gameUid1, ApplicationInfo.CATEGORY_GAME);
+ mockAppCategory(gamePkg1, gameUid1, ApplicationInfo.CATEGORY_GAME, userId);
String gamePkg2 = "game2";
int gameUid2 = DEFAULT_PACKAGE_UID + 4;
- mockAppCategory(gamePkg2, gameUid2, ApplicationInfo.CATEGORY_GAME);
+ mockAppCategory(gamePkg2, gameUid2, ApplicationInfo.CATEGORY_GAME, userId);
// non-game1 top and background with no-op
gameManagerService.mUidObserver.onUidStateChanged(
@@ -2470,15 +2493,17 @@
@Test
public void testGamePowerMode_gameAndNotGameApps_flagOff() throws Exception {
mSetFlagsRule.disableFlags(Flags.FLAG_DISABLE_GAME_MODE_WHEN_APP_TOP);
- GameManagerService gameManagerService = createServiceAndStartUser(USER_ID_1);
+ int userId = ActivityManager.getCurrentUser();
+ GameManagerService gameManagerService = new GameManagerService(mMockContext,
+ mTestLooper.getLooper());
String nonGamePkg1 = "not.game1";
int nonGameUid1 = DEFAULT_PACKAGE_UID + 1;
- mockAppCategory(nonGamePkg1, nonGameUid1, ApplicationInfo.CATEGORY_IMAGE);
+ mockAppCategory(nonGamePkg1, nonGameUid1, ApplicationInfo.CATEGORY_IMAGE, userId);
String gamePkg1 = "game1";
int gameUid1 = DEFAULT_PACKAGE_UID + 3;
- mockAppCategory(gamePkg1, gameUid1, ApplicationInfo.CATEGORY_GAME);
+ mockAppCategory(gamePkg1, gameUid1, ApplicationInfo.CATEGORY_GAME, userId);
// non-game1 top and background with no-op
gameManagerService.mUidObserver.onUidStateChanged(
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/GroupHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/GroupHelperTest.java
index 51f64ba..b97a268 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/GroupHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/GroupHelperTest.java
@@ -15,6 +15,7 @@
*/
package com.android.server.notification;
+import static android.app.Flags.FLAG_SORT_SECTION_BY_TIME;
import static android.app.Notification.COLOR_DEFAULT;
import static android.app.Notification.FLAG_AUTO_CANCEL;
import static android.app.Notification.FLAG_BUBBLE;
@@ -36,6 +37,7 @@
import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL;
import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT;
+import static com.android.server.notification.Flags.FLAG_NOTIFICATION_FORCE_GROUP_CONVERSATIONS;
import static com.android.server.notification.GroupHelper.AGGREGATE_GROUP_KEY;
import static com.android.server.notification.GroupHelper.AUTOGROUP_KEY;
import static com.android.server.notification.GroupHelper.BASE_FLAGS;
@@ -2204,7 +2206,7 @@
verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), anyString(),
eq(expectedGroupKey_silent), anyInt(), eq(getNotificationAttributes(BASE_FLAGS)));
verify(mCallback, times(AUTOGROUP_AT_COUNT)).addAutoGroup(anyString(),
- eq(expectedGroupKey_silent), eq(false));
+ eq(expectedGroupKey_silent), eq(true));
// Check that the alerting section group is removed
verify(mCallback, times(1)).removeAutoGroupSummary(anyInt(), eq(pkg),
@@ -2264,13 +2266,15 @@
notificationList);
// Check that channel1's notifications are moved to the silent section group
- expectedSummaryAttr = new NotificationAttributes(BASE_FLAGS,
- mSmallIcon, COLOR_DEFAULT, DEFAULT_VISIBILITY, DEFAULT_GROUP_ALERT,
- "TEST_CHANNEL_ID1");
- verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), anyString(),
- eq(expectedGroupKey_silent), anyInt(), eq(expectedSummaryAttr));
- verify(mCallback, times(AUTOGROUP_AT_COUNT/2 + 1)).addAutoGroup(anyString(),
- eq(expectedGroupKey_silent), eq(false));
+ // But not enough to auto-group => remove override group key
+ verify(mCallback, never()).addAutoGroupSummary(anyInt(), eq(pkg), anyString(),
+ anyString(), anyInt(), any());
+ verify(mCallback, never()).addAutoGroup(anyString(), anyString(), anyBoolean());
+ for (NotificationRecord record: notificationList) {
+ if (record.getChannel().getId().equals(channel1.getId())) {
+ assertThat(record.getSbn().getOverrideGroupKey()).isNull();
+ }
+ }
// Check that the alerting section group is not removed, only updated
expectedSummaryAttr = new NotificationAttributes(BASE_FLAGS,
@@ -2343,7 +2347,7 @@
verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), anyString(),
eq(expectedGroupKey_silent), anyInt(), eq(getNotificationAttributes(BASE_FLAGS)));
verify(mCallback, times(numSilentGroupNotifications)).addAutoGroup(anyString(),
- eq(expectedGroupKey_silent), eq(false));
+ eq(expectedGroupKey_silent), eq(true));
// Check that the alerting section group is removed
verify(mCallback, times(1)).removeAutoGroupSummary(anyInt(), eq(pkg),
@@ -2353,6 +2357,60 @@
}
@Test
+ @EnableFlags(FLAG_NOTIFICATION_FORCE_GROUPING)
+ public void testAutogroup_updateChannel_reachedMinAutogroupCount() {
+ final String pkg = "package";
+ final NotificationChannel channel1 = new NotificationChannel("TEST_CHANNEL_ID1",
+ "TEST_CHANNEL_ID1", IMPORTANCE_DEFAULT);
+ final NotificationChannel channel2 = new NotificationChannel("TEST_CHANNEL_ID2",
+ "TEST_CHANNEL_ID2", IMPORTANCE_LOW);
+ final List<NotificationRecord> notificationList = new ArrayList<>();
+ // Post notifications with different channels that would autogroup in different sections
+ NotificationRecord r;
+ // Not enough notifications to autogroup initially
+ for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) {
+ if (i % 2 == 0) {
+ r = getNotificationRecord(pkg, i, String.valueOf(i),
+ UserHandle.SYSTEM, null, false, channel1);
+ } else {
+ r = getNotificationRecord(pkg, i, String.valueOf(i),
+ UserHandle.SYSTEM, null, false, channel2);
+ }
+ notificationList.add(r);
+ mGroupHelper.onNotificationPosted(r, false);
+ }
+ verify(mCallback, never()).addAutoGroupSummary(anyInt(), anyString(), anyString(),
+ anyString(), anyInt(), any());
+ verify(mCallback, never()).addAutoGroup(anyString(), anyString(), anyBoolean());
+ verify(mCallback, never()).removeAutoGroup(anyString());
+ verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString(), anyString());
+ verify(mCallback, never()).updateAutogroupSummary(anyInt(), anyString(), anyString(),
+ any());
+ Mockito.reset(mCallback);
+
+ // Update channel1's importance
+ final String expectedGroupKey_silent = GroupHelper.getFullAggregateGroupKey(pkg,
+ AGGREGATE_GROUP_KEY + "SilentSection", UserHandle.SYSTEM.getIdentifier());
+ channel1.setImportance(IMPORTANCE_LOW);
+ for (NotificationRecord record: notificationList) {
+ if (record.getChannel().getId().equals(channel1.getId())) {
+ record.updateNotificationChannel(channel1);
+ }
+ }
+ mGroupHelper.onChannelUpdated(UserHandle.SYSTEM.getIdentifier(), pkg, channel1,
+ notificationList);
+
+ // Check that channel1's notifications are moved to the silent section & autogroup all
+ NotificationAttributes expectedSummaryAttr = new NotificationAttributes(BASE_FLAGS,
+ mSmallIcon, COLOR_DEFAULT, DEFAULT_VISIBILITY, DEFAULT_GROUP_ALERT,
+ "TEST_CHANNEL_ID1");
+ verify(mCallback, times(AUTOGROUP_AT_COUNT)).addAutoGroup(anyString(),
+ eq(expectedGroupKey_silent), eq(true));
+ verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), anyString(),
+ eq(expectedGroupKey_silent), anyInt(), eq(expectedSummaryAttr));
+ }
+
+ @Test
@EnableFlags({FLAG_NOTIFICATION_FORCE_GROUPING,
Flags.FLAG_NOTIFICATION_FORCE_GROUP_SINGLETONS})
public void testNoGroup_singletonGroup_underLimit() {
@@ -2519,7 +2577,11 @@
assertThat(cachedSummary).isNull();
}
- private void checkNonGroupableNotifications() {
+ @Test
+ @EnableFlags(FLAG_NOTIFICATION_FORCE_GROUPING)
+ @DisableFlags(FLAG_NOTIFICATION_FORCE_GROUP_CONVERSATIONS)
+ public void testNonGroupableNotifications() {
+ // Check that there is no valid section for: conversations, calls, foreground services
NotificationRecord notification_conversation = mock(NotificationRecord.class);
when(notification_conversation.isConversation()).thenReturn(true);
assertThat(GroupHelper.getSection(notification_conversation)).isNull();
@@ -2592,8 +2654,6 @@
"", false, recsChannel);
assertThat(GroupHelper.getSection(notification_recs).mName).isEqualTo(
"AlertingSection");
-
- checkNonGroupableNotifications();
}
@Test
@@ -2638,8 +2698,86 @@
"", false, recsChannel);
assertThat(GroupHelper.getSection(notification_recs).mName).isEqualTo(
"RecsSection");
+ }
- checkNonGroupableNotifications();
+ @Test
+ @EnableFlags({FLAG_NOTIFICATION_FORCE_GROUPING, FLAG_NOTIFICATION_FORCE_GROUP_CONVERSATIONS})
+ public void testNonGroupableNotifications_forceGroupConversations() {
+ // Check that there is no valid section for: calls, foreground services
+ NotificationRecord notification_call = spy(getNotificationRecord(mPkg, 0, "", mUser,
+ "", false, IMPORTANCE_LOW));
+ Notification n = mock(Notification.class);
+ StatusBarNotification sbn = spy(getSbn("package", 0, "0", UserHandle.SYSTEM));
+ when(notification_call.isConversation()).thenReturn(false);
+ when(notification_call.getNotification()).thenReturn(n);
+ when(notification_call.getSbn()).thenReturn(sbn);
+ when(sbn.getNotification()).thenReturn(n);
+ when(n.isStyle(Notification.CallStyle.class)).thenReturn(true);
+ assertThat(GroupHelper.getSection(notification_call)).isNull();
+
+ NotificationRecord notification_colorFg = spy(getNotificationRecord(mPkg, 0, "", mUser,
+ "", false, IMPORTANCE_LOW));
+ sbn = spy(getSbn("package", 0, "0", UserHandle.SYSTEM));
+ n = mock(Notification.class);
+ when(notification_colorFg.isConversation()).thenReturn(false);
+ when(notification_colorFg.getNotification()).thenReturn(n);
+ when(notification_colorFg.getSbn()).thenReturn(sbn);
+ when(sbn.getNotification()).thenReturn(n);
+ when(n.isForegroundService()).thenReturn(true);
+ when(n.isColorized()).thenReturn(true);
+ when(n.isStyle(Notification.CallStyle.class)).thenReturn(false);
+ assertThat(GroupHelper.getSection(notification_colorFg)).isNull();
+ }
+
+ @Test
+ @EnableFlags({FLAG_NOTIFICATION_FORCE_GROUPING, FLAG_NOTIFICATION_FORCE_GROUP_CONVERSATIONS})
+ @DisableFlags(FLAG_SORT_SECTION_BY_TIME)
+ public void testConversationGroupSections_disableSortSectionByTime() {
+ // Check that there are separate sections for conversations: alerting and silent
+ NotificationRecord notification_conversation_silent = getNotificationRecord(mPkg, 0, "",
+ mUser, "", false, IMPORTANCE_LOW);
+ notification_conversation_silent = spy(notification_conversation_silent);
+ when(notification_conversation_silent.isConversation()).thenReturn(true);
+ assertThat(GroupHelper.getSection(notification_conversation_silent).mName).isEqualTo(
+ "PeopleSection(silent)");
+
+ // Check that there is a correct section for conversations
+ NotificationRecord notification_conversation_alerting = getNotificationRecord(mPkg, 0, "",
+ mUser, "", false, IMPORTANCE_DEFAULT);
+ notification_conversation_alerting = spy(notification_conversation_alerting);
+ when(notification_conversation_alerting.isConversation()).thenReturn(true);
+ assertThat(GroupHelper.getSection(notification_conversation_alerting).mName).isEqualTo(
+ "PeopleSection(alerting)");
+ }
+
+ @Test
+ @EnableFlags({FLAG_NOTIFICATION_FORCE_GROUPING,
+ FLAG_NOTIFICATION_FORCE_GROUP_CONVERSATIONS,
+ FLAG_SORT_SECTION_BY_TIME})
+ public void testConversationGroupSections() {
+ // Check that there is a single section for silent/alerting conversations
+ NotificationRecord notification_conversation_silent = getNotificationRecord(mPkg, 0, "",
+ mUser, "", false, IMPORTANCE_LOW);
+ notification_conversation_silent = spy(notification_conversation_silent);
+ when(notification_conversation_silent.isConversation()).thenReturn(true);
+ assertThat(GroupHelper.getSection(notification_conversation_silent).mName).isEqualTo(
+ "PeopleSection");
+
+ NotificationRecord notification_conversation_alerting = getNotificationRecord(mPkg, 0, "",
+ mUser, "", false, IMPORTANCE_DEFAULT);
+ notification_conversation_alerting = spy(notification_conversation_alerting);
+ when(notification_conversation_alerting.isConversation()).thenReturn(true);
+ assertThat(GroupHelper.getSection(notification_conversation_alerting).mName).isEqualTo(
+ "PeopleSection");
+
+ // Check that there is a section for priority conversations
+ NotificationRecord notification_conversation_prio = getNotificationRecord(mPkg, 0, "",
+ mUser, "", false, IMPORTANCE_DEFAULT);
+ notification_conversation_prio = spy(notification_conversation_prio);
+ when(notification_conversation_prio.isConversation()).thenReturn(true);
+ notification_conversation_prio.getChannel().setImportantConversation(true);
+ assertThat(GroupHelper.getSection(notification_conversation_prio).mName).isEqualTo(
+ "PeopleSection(priority)");
}
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
index 6f10370..c1e3f47 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
@@ -2186,6 +2186,80 @@
}
@Test
+ @EnableFlags({FLAG_MODES_API, FLAG_MODES_UI})
+ public void testReadXml_upgradeToModesUi_resetsImplicitRuleIcon() throws Exception {
+ setupZenConfig();
+ mZenModeHelper.mConfig.automaticRules.clear();
+
+ ZenRule implicitRuleWithModesUi = expectedImplicitRule("pkg",
+ ZEN_MODE_IMPORTANT_INTERRUPTIONS, POLICY, null);
+
+ // Add one implicit rule in the pre-MODES_UI configuration.
+ ZenRule implicitRuleBeforeModesUi = implicitRuleWithModesUi.copy();
+ implicitRuleBeforeModesUi.iconResName = "pkg_icon";
+ mZenModeHelper.mConfig.automaticRules.put(implicitRuleBeforeModesUi.id,
+ implicitRuleBeforeModesUi);
+ // Plus one other normal rule.
+ ZenRule anotherRule = newZenRule("other_pkg", Instant.now(), null);
+ anotherRule.id = "other_rule";
+ anotherRule.iconResName = "other_icon";
+ anotherRule.type = TYPE_IMMERSIVE;
+ mZenModeHelper.mConfig.automaticRules.put(anotherRule.id, anotherRule);
+
+ // Write with pre-modes-ui = (modes_api) version, then re-read.
+ ByteArrayOutputStream baos = writeXmlAndPurge(ZenModeConfig.XML_VERSION_MODES_API);
+ TypedXmlPullParser parser = Xml.newFastPullParser();
+ parser.setInput(new BufferedInputStream(
+ new ByteArrayInputStream(baos.toByteArray())), null);
+ parser.nextTag();
+ mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL);
+
+ // Implicit rule was updated.
+ assertThat(mZenModeHelper.mConfig.automaticRules.get(implicitRuleBeforeModesUi.id))
+ .isEqualTo(implicitRuleWithModesUi);
+
+ // The other rule was untouched.
+ assertThat(mZenModeHelper.mConfig.automaticRules.get(anotherRule.id))
+ .isEqualTo(anotherRule);
+ }
+
+ @Test
+ @EnableFlags({FLAG_MODES_API, FLAG_MODES_UI})
+ public void testReadXml_onModesUi_implicitRulesUntouched() throws Exception {
+ setupZenConfig();
+ mZenModeHelper.mConfig.automaticRules.clear();
+
+ // Add one implicit rule already in its post-modes-UI configuration, also customized with
+ // an icon;
+ ZenRule implicitRuleWithModesUi = expectedImplicitRule("pkg",
+ ZEN_MODE_IMPORTANT_INTERRUPTIONS, POLICY, null);
+ implicitRuleWithModesUi.iconResName = "icon_chosen_by_user";
+ mZenModeHelper.mConfig.automaticRules.put(implicitRuleWithModesUi.id,
+ implicitRuleWithModesUi);
+
+ // Plus one other normal rule.
+ ZenRule anotherRule = newZenRule("other_pkg", Instant.now(), null);
+ anotherRule.id = "other_rule";
+ anotherRule.iconResName = "other_icon";
+ anotherRule.type = TYPE_IMMERSIVE;
+ mZenModeHelper.mConfig.automaticRules.put(anotherRule.id, anotherRule);
+
+ // Write with modes_ui version, then re-read.
+ ByteArrayOutputStream baos = writeXmlAndPurge(ZenModeConfig.XML_VERSION_MODES_UI);
+ TypedXmlPullParser parser = Xml.newFastPullParser();
+ parser.setInput(new BufferedInputStream(
+ new ByteArrayInputStream(baos.toByteArray())), null);
+ parser.nextTag();
+ mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL);
+
+ // Both rules were untouched
+ assertThat(mZenModeHelper.mConfig.automaticRules.get(implicitRuleWithModesUi.id))
+ .isEqualTo(implicitRuleWithModesUi);
+ assertThat(mZenModeHelper.mConfig.automaticRules.get(anotherRule.id))
+ .isEqualTo(anotherRule);
+ }
+
+ @Test
public void testCountdownConditionSubscription() throws Exception {
ZenModeConfig config = new ZenModeConfig();
mZenModeHelper.mConfig = config;
@@ -6852,7 +6926,9 @@
rule.zenPolicy = policy;
rule.pkg = ownerPkg;
rule.name = CUSTOM_APP_LABEL;
- rule.iconResName = ICON_RES_NAME;
+ if (!Flags.modesUi()) {
+ rule.iconResName = ICON_RES_NAME;
+ }
rule.triggerDescription = mContext.getString(R.string.zen_mode_implicit_trigger_description,
CUSTOM_APP_LABEL);
rule.type = AutomaticZenRule.TYPE_OTHER;
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
index 9950541..b6e393d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
@@ -39,10 +39,8 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
-import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_RECENTS;
import static com.android.server.wm.WindowContainer.POSITION_TOP;
import static org.junit.Assert.assertEquals;
@@ -51,9 +49,7 @@
import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeFalse;
import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
import android.graphics.Rect;
import android.os.Binder;
@@ -377,41 +373,6 @@
}
@Test
- public void testDelayWhileRecents() {
- final DisplayContent dc = createNewDisplay(Display.STATE_ON);
- doReturn(false).when(dc).onDescendantOrientationChanged(any());
- final Task task = createTask(dc);
-
- // Simulate activity1 launches activity2.
- final ActivityRecord activity1 = createActivityRecord(task);
- activity1.setVisible(true);
- activity1.setVisibleRequested(false);
- activity1.allDrawn = true;
- final ActivityRecord activity2 = createActivityRecord(task);
- activity2.setVisible(false);
- activity2.setVisibleRequested(true);
- activity2.allDrawn = true;
-
- dc.mClosingApps.add(activity1);
- dc.mOpeningApps.add(activity2);
- dc.prepareAppTransition(TRANSIT_OPEN);
- assertTrue(dc.mAppTransition.containsTransitRequest(TRANSIT_OPEN));
-
- // Wait until everything in animation handler get executed to prevent the exiting window
- // from being removed during WindowSurfacePlacer Traversal.
- waitUntilHandlersIdle();
-
- // Start recents
- doReturn(true).when(task)
- .isSelfAnimating(anyInt(), eq(ANIMATION_TYPE_RECENTS));
-
- dc.mAppTransitionController.handleAppTransitionReady();
-
- verify(activity1, never()).commitVisibility(anyBoolean(), anyBoolean(), anyBoolean());
- verify(activity2, never()).commitVisibility(anyBoolean(), anyBoolean(), anyBoolean());
- }
-
- @Test
public void testGetAnimationStyleResId() {
// Verify getAnimationStyleResId will return as LayoutParams.windowAnimations when without
// specifying window type.
diff --git a/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimatorTest.java b/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimatorTest.java
index 6c8a7ac..9981a4d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimatorTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimatorTest.java
@@ -23,7 +23,6 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verifyZeroInteractions;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION;
-import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_RECENTS;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -96,18 +95,18 @@
@Test
public void testRunAnimation() {
mAnimatable.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */,
- ANIMATION_TYPE_RECENTS);
+ ANIMATION_TYPE_APP_TRANSITION);
final ArgumentCaptor<OnAnimationFinishedCallback> callbackCaptor = ArgumentCaptor.forClass(
OnAnimationFinishedCallback.class);
assertAnimating(mAnimatable);
verify(mTransaction).reparent(eq(mAnimatable.mSurface), eq(mAnimatable.mLeash));
- verify(mSpec).startAnimation(any(), any(), eq(ANIMATION_TYPE_RECENTS),
+ verify(mSpec).startAnimation(any(), any(), eq(ANIMATION_TYPE_APP_TRANSITION),
callbackCaptor.capture());
- callbackCaptor.getValue().onAnimationFinished(ANIMATION_TYPE_RECENTS, mSpec);
+ callbackCaptor.getValue().onAnimationFinished(ANIMATION_TYPE_APP_TRANSITION, mSpec);
assertNotAnimating(mAnimatable);
assertTrue(mAnimatable.mFinishedCallbackCalled);
- assertEquals(ANIMATION_TYPE_RECENTS, mAnimatable.mFinishedAnimationType);
+ assertEquals(ANIMATION_TYPE_APP_TRANSITION, mAnimatable.mFinishedAnimationType);
verify(mTransaction).remove(eq(mAnimatable.mLeash));
// TODO: Verify reparenting once we use mPendingTransaction to reparent it back
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java b/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java
index d537bd7..88ce3a6 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java
@@ -40,14 +40,12 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
-import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_RECENTS;
import static com.android.server.wm.WindowStateAnimator.PRESERVED_SURFACE_LAYER;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
import android.graphics.PixelFormat;
import android.graphics.Rect;
@@ -405,30 +403,6 @@
}
@Test
- public void testAssignWindowLayers_ForImeOnAppWithRecentsAnimating() {
- final WindowState imeAppTarget = createWindow(null, TYPE_APPLICATION,
- mAppWindow.mActivityRecord, "imeAppTarget");
- mDisplayContent.setImeInputTarget(imeAppTarget);
- mDisplayContent.setImeLayeringTarget(imeAppTarget);
- mDisplayContent.setImeControlTarget(imeAppTarget);
- mDisplayContent.updateImeParent();
-
- // Simulate the ime layering target task is animating with recents animation.
- final Task imeAppTargetTask = imeAppTarget.getTask();
- final SurfaceAnimator imeTargetTaskAnimator = imeAppTargetTask.mSurfaceAnimator;
- spyOn(imeTargetTaskAnimator);
- doReturn(ANIMATION_TYPE_RECENTS).when(imeTargetTaskAnimator).getAnimationType();
- doReturn(true).when(imeTargetTaskAnimator).isAnimating();
-
- mDisplayContent.assignChildLayers(mTransaction);
-
- // Ime should on top of the application window when in recents animation and keep
- // attached on app.
- assertTrue(mDisplayContent.shouldImeAttachedToApp());
- assertWindowHigher(mImeWindow, imeAppTarget);
- }
-
- @Test
public void testAssignWindowLayers_ForImeOnPopupImeLayeringTarget() {
final WindowState imeAppTarget = createWindow(null, TYPE_APPLICATION,
mAppWindow.mActivityRecord, "imeAppTarget");